{"info":{"_postman_id":"69b0cfee-802b-4727-bd7b-b013e89bb889","name":"VERIFIED Crypto Checkout API","description":"<html><head></head><body><p>Official Postman collection for the VERIFIED Crypto Checkout API.</p>\n<h2 id=\"quick-start\">Quick Start</h2>\n<ol>\n<li>Set <code>merchant_wallet</code> to your Polygon USDC wallet address (starts with 0x, 42 chars)</li>\n<li>Set <code>webhook_secret</code> to a strong random string (the SAME value must be set in your webhook handler's environment)</li>\n<li>Set <code>success_url</code> and <code>cancel_url</code> to your redirect URLs</li>\n<li>Run <strong>Create Session (Full)</strong> — test script auto-saves <code>session_id</code> and <code>checkout_url</code></li>\n<li>Redirect your customer to <code>checkout_url</code> exactly as returned — do NOT modify it</li>\n<li>Verify payments via HMAC-SHA256 signed webhooks</li>\n</ol>\n<h2 id=\"key-rules\">Key Rules</h2>\n<ul>\n<li>No API key required — merchant identity = wallet address</li>\n<li><code>order_id</code> is required on every session creation request</li>\n<li><code>webhook_secret</code> is required whenever <code>webhook_url</code> is set</li>\n<li>Sessions expire after 30 minutes</li>\n<li>Never modify <code>checkout_url</code> after receiving it</li>\n<li>Webhooks are the authoritative payment signal — only treat <code>event: payment.confirmed</code> / <code>status: confirmed</code> as paid</li>\n<li>No sandbox — all sessions are live. Use small amounts ($1–$10) for testing.</li>\n</ul>\n<h2 id=\"domains\">Domains</h2>\n<ul>\n<li><strong>API</strong>: <a href=\"https://payapi.verifiedcryptocheckout.com\">https://payapi.verifiedcryptocheckout.com</a></li>\n<li><strong>Hosted Checkout</strong>: <a href=\"https://pay.verifiedcryptocheckout.com\">https://pay.verifiedcryptocheckout.com</a></li>\n</ul>\n<h2 id=\"support\">Support</h2>\n<p><a href=\"mailto:cryptocheckout@verifiedcreditcardprocessing.com\">cryptocheckout@verifiedcreditcardprocessing.com</a></p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"53498215","collectionId":"69b0cfee-802b-4727-bd7b-b013e89bb889","publishedId":"2sBXinHAaA","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-04-02T00:28:09.000Z"},"item":[{"name":"SESSION CREATION","item":[{"name":"1 · Create Session — Full Request ✅","id":"8bf171e9-932e-4883-a6d0-b30b1f41c937","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json","description":"<p>Required on all POST requests.</p>\n"},{"key":"Idempotency-Key","value":"c626fcca-4fc8-477e-b551-63b1101ce785","description":"<p>Optional but recommended on retries. If same key is reused, the API may return the existing session instead of creating a duplicate.</p>\n"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 125.50,\n  \"currency\": \"USD\",\n  \"wallet\": \"0xYOUR_POLYGON_USDC_WALLET_ADDRESS\",\n  \"order_id\": \"ORDER-1001\",\n  \"customer_email\": \"customer@example.com\",\n  \"success_url\": \"https://yourstore.com/order/success\",\n  \"cancel_url\": \"https://yourstore.com/order/cancelled\",\n  \"webhook_url\": \"https://yourstore.com/api/payment-webhook\",\n  \"webhook_secret\": \"CHANGE_ME_TO_A_STRONG_RANDOM_STRING\",\n  \"metadata\": {\n    \"cart_id\": \"CART-8821\",\n    \"customer_id\": \"USR-442\"\n  }\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"8bf171e9-932e-4883-a6d0-b30b1f41c937"},{"name":"2 · Create Session — Minimal (Required Fields Only)","id":"ba24780b-6383-4e8e-8385-78125eaf10db","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 10.00,\n  \"currency\": \"USD\",\n  \"wallet\": \"0xYOUR_POLYGON_USDC_WALLET_ADDRESS\",\n  \"order_id\": \"ORDER-001\",\n  \"success_url\": \"https://yourstore.com/order/success\",\n  \"cancel_url\": \"https://yourstore.com/order/cancelled\",\n  \"webhook_url\": \"https://yourstore.com/api/payment-webhook\",\n  \"webhook_secret\": \"CHANGE_ME_TO_A_STRONG_RANDOM_STRING\"\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"ba24780b-6383-4e8e-8385-78125eaf10db"},{"name":"3 · Create Session — With Provider Hint","id":"9eb1c7f5-742f-45cf-8487-f6d9ed00fe93","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"2d702ee2-ffef-4925-b600-9b6cea170e29"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 75.00,\n  \"currency\": \"USD\",\n  \"wallet\": \"0xYOUR_POLYGON_USDC_WALLET_ADDRESS\",\n  \"order_id\": \"ORDER-PROV-746\",\n  \"customer_email\": \"customer@example.com\",\n  \"success_url\": \"https://yourstore.com/order/success\",\n  \"cancel_url\": \"https://yourstore.com/order/cancelled\",\n  \"webhook_url\": \"https://yourstore.com/api/payment-webhook\",\n  \"webhook_secret\": \"CHANGE_ME_TO_A_STRONG_RANDOM_STRING\",\n  \"provider\": \"moonpay\"\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"9eb1c7f5-742f-45cf-8487-f6d9ed00fe93"}],"id":"14066319-4741-4448-9bc6-65044d2389ae","description":"<p>Create payment sessions — the first and only required step to initiate a checkout flow.</p>\n<p>Your server calls <code>POST /v1/session</code> with the order amount, currency, merchant wallet, and order details. VERIFIED validates the request, configures routing and settlement parameters, and returns a fully prepared <code>checkout_url</code>. Redirect the customer to that URL to begin the payment flow.</p>\n<p>⚠️ Session creation must always be performed server-side — never from client-side JavaScript. Although no API key is required, server-side creation prevents tampering with order amounts, wallet values, and metadata.</p>\n","_postman_id":"14066319-4741-4448-9bc6-65044d2389ae"},{"name":"DEBUG","item":[{"name":"4 · Inspect URL Structure — GET /test-config","id":"83e5ebe2-152d-487e-a421-8bfd1c3f3f4d","request":{"method":"GET","header":[],"url":"https://payapi.verifiedcryptocheckout.com/test-config?wallet=0xYOUR_POLYGON_USDC_WALLET_ADDRESS&amount=10&currency=USD&order_id=TEST-DEBUG-001&customer_email=customer@example.com&success_url=https://yourstore.com/order/success&cancel_url=https://yourstore.com/order/cancelled&webhook_url=https://yourstore.com/api/payment-webhook","urlObject":{"path":["test-config"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[{"key":"wallet","value":"0xYOUR_POLYGON_USDC_WALLET_ADDRESS"},{"key":"amount","value":"10"},{"key":"currency","value":"USD"},{"key":"order_id","value":"TEST-DEBUG-001"},{"key":"customer_email","value":"customer@example.com"},{"key":"success_url","value":"https://yourstore.com/order/success"},{"key":"cancel_url","value":"https://yourstore.com/order/cancelled"},{"key":"webhook_url","value":"https://yourstore.com/api/payment-webhook"}],"variable":[]}},"response":[],"_postman_id":"83e5ebe2-152d-487e-a421-8bfd1c3f3f4d"}],"id":"0c2b9d33-919c-400a-905a-c1453943b6f6","description":"<p>Developer helper endpoint for inspecting generated URL structures without creating a live payment session.</p>\n<p>Use <code>GET /test-config</code> during development to validate your wallet address format, confirm redirect URLs are correctly formed, and inspect the checkout_url structure before going live.</p>\n<p>⚠️ This endpoint does NOT create a live session and does NOT accept real payments.</p>\n","_postman_id":"0c2b9d33-919c-400a-905a-c1453943b6f6"},{"name":"ERROR HANDLING","item":[{"name":"5 · Error — Missing Wallet (expect 400)","id":"1eca86b7-eb8c-4bff-9260-10b30c1fcae8","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 100.00,\n  \"currency\": \"USD\",\n  \"order_id\": \"ORDER-ERROR-TEST\",\n  \"success_url\": \"https://example.com/success\",\n  \"cancel_url\": \"https://example.com/cancel\"\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"1eca86b7-eb8c-4bff-9260-10b30c1fcae8"},{"name":"6 · Error — Invalid Wallet Format (expect 400)","id":"2528c108-3b35-47b3-b2c8-e485f7870d1b","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 100.00,\n  \"currency\": \"USD\",\n  \"wallet\": \"INVALID_WALLET_NO_0x_PREFIX\",\n  \"order_id\": \"ORDER-WALLET-ERROR\",\n  \"success_url\": \"https://example.com/success\",\n  \"cancel_url\": \"https://example.com/cancel\"\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"2528c108-3b35-47b3-b2c8-e485f7870d1b"},{"name":"7 · Error — Wrong HTTP Method GET /v1/session (expect 405)","id":"71702ee9-6d0e-42d5-8a23-cc799d501e71","request":{"method":"GET","header":[],"url":"https://payapi.verifiedcryptocheckout.com/v1/session","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"71702ee9-6d0e-42d5-8a23-cc799d501e71"},{"name":"8 · Error — Rate Limit Exceeded (expect 429)","id":"382e344c-b12b-3788-fd2d-b3a8d628a24f","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 10.00,\n  \"currency\": \"USD\",\n  \"wallet\": \"0xYOUR_POLYGON_USDC_WALLET_ADDRESS\",\n  \"order_id\": \"RATE-LIMIT-TEST\",\n  \"success_url\": \"https://yourstore.com/order/success\",\n  \"cancel_url\": \"https://yourstore.com/order/cancelled\"\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"382e344c-b12b-3788-fd2d-b3a8d628a24f"}],"id":"75255bb2-c525-4126-b095-df8614a68c29","description":"<p>Reference requests demonstrating common error scenarios and the expected HTTP status codes.</p>\n<p>All VERIFIED API errors return JSON with <code>ok: false</code>, <code>code</code>, <code>error</code>, and <code>message</code> fields.</p>\n","_postman_id":"75255bb2-c525-4126-b095-df8614a68c29"},{"name":"WEBHOOK REFERENCE","item":[{"name":"9 · Webhook Payload — payment.confirmed (Reference)","id":"e2373377-204c-48a5-90da-1a21fb1fe05f","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json","description":"<p>Webhook payload is always JSON.</p>\n"},{"key":"X-VCC-Timestamp","value":"1742040731","description":"<p>Unix timestamp (seconds). Reject if &gt;5 minutes from current server time.</p>\n"},{"key":"X-VCC-Signature","value":"a3f8c2e1d7b9f4e2c1a8d7f3b2e9c4a1d8f7e2b3c9a4f1e8d7b2c3f9a4e1d8","description":"<p>HMAC-SHA256(webhook_secret, '{X-VCC-Timestamp}.{raw_body}'). Use constant-time comparison.</p>\n"}],"body":{"mode":"raw","raw":"{\n  \"event\": \"payment.confirmed\",\n  \"session_id\": \"\",\n  \"order_id\": \"ORDER-1001\",\n  \"amount\": 125.50,\n  \"currency\": \"USD\",\n  \"wallet\": \"0xYOUR_POLYGON_USDC_WALLET_ADDRESS\",\n  \"tx_hash\": \"0x8f12c4d9e3b7a2f1c6d8e5b4a3f2e1d9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4\",\n  \"status\": \"confirmed\",\n  \"timestamp\": \"2026-03-29T14:32:11Z\",\n  \"amount_forwarded\": 120.48,\n  \"confirmations\": 12,\n  \"raw\": {\n    \"provider\": \"moonpay\",\n    \"provider_tx_id\": \"mp_8472afe1\",\n    \"settlement_chain\": \"polygon\",\n    \"settlement_token\": \"USDC\",\n    \"block_number\": 56234118\n  }\n}","options":{"raw":{"language":"json"}}},"url":"https://yourstore.com/api/payment-webhook","urlObject":{"host":["https://yourstore.com/api/payment-webhook"],"query":[],"variable":[]}},"response":[],"_postman_id":"e2373377-204c-48a5-90da-1a21fb1fe05f"}],"id":"b58ab8e3-dc15-427f-b2b6-b62041903146","description":"<p>Reference showing the structure of the HMAC-signed webhook POST that VERIFIED sends to your <code>webhook_url</code> when payment is confirmed on-chain.</p>\n<p>Webhooks are the authoritative payment confirmation signal — never mark an order as paid based on the <code>success_url</code> redirect alone. Verify the HMAC-SHA256 signature on every incoming webhook before updating order state.</p>\n<p>Only treat <code>event: payment.confirmed</code> / <code>status: confirmed</code> as paid. Other event/status combinations (e.g. <code>pending</code>) must not move the order to paid.</p>\n","_postman_id":"b58ab8e3-dc15-427f-b2b6-b62041903146"},{"name":"THANK-YOU PAGE — Hosted Waiting Room (NEW)","item":[{"name":"14 · Register Thank-You Session — POST /t/api/register","id":"d5d9e085-a1e8-4424-acb2-355d5f7505e9","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json","description":"<p>Required.</p>\n"}],"body":{"mode":"raw","raw":"{\n  \"token\": \"Zk3q8rW1xY5tP0aN7cB2dE9fH4jL6mSv\",\n  \"status_url\": \"https://yourstore.com/wp-json/vcc-ty/v1/status/1234\",\n  \"secret\": \"uV8wX2yZ4aB6cD0eF1gH3iJ5kL7mN9pQ\",\n  \"pay_url\": \"https://pay.verifiedcryptocheckout.com/process-payment.php?address=0xADDRESS_IN&amount=125.50&provider=stripe&email=customer%40example.com&currency=USD\",\n  \"retry_url\": \"https://pay.verifiedcryptocheckout.com/pay.php?address=0xADDRESS_IN&amount=125.50&currency=USD&email=customer%40example.com\",\n  \"display\": \"standard\",\n  \"brand\": {\n    \"name\": \"Merchant Store\",\n    \"logo\": \"https://yourstore.com/wp-content/uploads/logo.png\",\n    \"accent\": \"#1f6f54\",\n    \"bg\": \"#ffffff\",\n    \"mode\": \"newtab\"\n  },\n  \"ttl\": 172800\n}","options":{"raw":{"language":"json"}}},"url":"https://pay.verifiedcryptocheckout.com/t/api/register","urlObject":{"protocol":"https","path":["t","api","register"],"host":["pay","verifiedcryptocheckout","com"],"query":[],"variable":[]}},"response":[],"_postman_id":"d5d9e085-a1e8-4424-acb2-355d5f7505e9"},{"name":"15 · Waiting Room / Receipt Page — GET /t/{token}","id":"0107223c-b34f-4931-8d4f-9deb44e37416","request":{"method":"GET","header":[],"url":"https://pay.verifiedcryptocheckout.com/t/:ty_token","urlObject":{"protocol":"https","path":["t",":ty_token"],"host":["pay","verifiedcryptocheckout","com"],"query":[],"variable":[{"type":"any","key":"ty_token"}]}},"response":[],"_postman_id":"0107223c-b34f-4931-8d4f-9deb44e37416"},{"name":"16 · Poll Payment Status — GET /t/api/status/{token}","id":"64e6c3aa-9d9e-48ce-8b81-711c6be789c4","request":{"method":"GET","header":[],"url":"https://pay.verifiedcryptocheckout.com/t/api/status/:ty_token","urlObject":{"protocol":"https","path":["t","api","status",":ty_token"],"host":["pay","verifiedcryptocheckout","com"],"query":[],"variable":[{"type":"any","key":"ty_token"}]}},"response":[],"_postman_id":"64e6c3aa-9d9e-48ce-8b81-711c6be789c4"},{"name":"17 · Merchant Status Endpoint Contract — GET (YOUR server)","id":"e7846610-df80-4ac5-a084-7cfa09e5fbfa","request":{"method":"GET","header":[{"key":"X-VCC-TY-Secret","value":"uV8wX2yZ4aB6cD0eF1gH3iJ5kL7mN9pQ","description":"<p>The per-order secret supplied at registration. Compare against your stored hash using a constant-time comparison. Missing/wrong → return 401/403.</p>\n"},{"key":"Accept","value":"application/json"}],"url":"https://yourstore.com/wp-json/vcc-ty/v1/status/:order_id","urlObject":{"protocol":"https","path":["wp-json","vcc-ty","v1","status",":order_id"],"host":["yourstore","com"],"query":[],"variable":[{"type":"any","key":"order_id"}]}},"response":[],"_postman_id":"e7846610-df80-4ac5-a084-7cfa09e5fbfa"}],"id":"e6b91d47-2f8a-4c05-9d3e-8b1a67f0c214","description":"<p><strong>NEW FEATURE (rolling out)</strong> — A hosted, merchant-branded waiting-room / thank-you page on <code>pay.verifiedcryptocheckout.com/t/{token}</code>.</p>\n<p>Instead of leaving the customer stranded on the payment provider's page, the customer lands on this page immediately after checkout. It opens the provider payment window, polls order status, and transforms into a branded receipt (order details, TXID, on-chain verification badge) the moment payment confirms — <strong>without depending on any provider redirect</strong>. Confirmation is driven by the same server-side payment signal that already completes the order, observed via polling.</p>\n<p><strong>How it fits the flow:</strong></p>\n<ol>\n<li>Your server registers a thank-you session (<code>POST /t/api/register</code>) after creating the order/payment address</li>\n<li>You redirect the customer to <code>https://pay.verifiedcryptocheckout.com/t/{token}</code> instead of the provider URL directly</li>\n<li>The page opens the provider payment window and polls <code>GET /t/api/status/{token}</code></li>\n<li>VERIFIED calls <strong>your</strong> status endpoint server-side (authenticated with the per-order secret) — your domain never appears in any browser-visible request and is never shared with the payment provider</li>\n<li>When your endpoint reports <code>paid</code>, the page renders the receipt</li>\n</ol>\n<p><strong>Privacy guarantees:</strong></p>\n<ul>\n<li>The <code>/t/{token}</code> URL is never sent to PayGate or any payment provider</li>\n<li>Your store's domain, order contents, and customer details travel only: your server → VERIFIED edge → customer's browser</li>\n<li>Tokens are 192-bit random, stored hashed, and expire after 48 hours</li>\n</ul>\n<p><strong>WooCommerce plugin merchants:</strong> the plugin (upcoming release) does all of this automatically — registration, the status endpoint, branding from your site logo/title, and settings under WooCommerce → Settings. Nothing in this folder needs to be called manually.</p>\n<p><strong>API merchants:</strong> these endpoints document the contract if you want the same experience for <code>/v1/session</code> flows (planned; the session flow's <code>success_url</code> continues to work as-is).</p>\n<p>⚠️ <strong>Status:</strong> do not integrate against these endpoints until the <code>/t/*</code> route is announced as live. Endpoint shapes below are the implementation contract.</p>\n","_postman_id":"e6b91d47-2f8a-4c05-9d3e-8b1a67f0c214"},{"name":"10 · Payment Links — Sharing checkout_url (Reference)","id":"14040f6b-6659-9c9c-3dbf-51f35c56d07d","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"b6897259-9a0d-4d4a-bb52-233d1cf2d06a"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 75.00,\n  \"currency\": \"USD\",\n  \"wallet\": \"0xYOUR_POLYGON_USDC_WALLET_ADDRESS\",\n  \"order_id\": \"ORDER-644\",\n  \"customer_email\": \"customer@example.com\",\n  \"success_url\": \"https://yourstore.com/order/success\",\n  \"cancel_url\": \"https://yourstore.com/order/cancelled\",\n  \"webhook_url\": \"https://yourstore.com/api/payment-webhook\",\n  \"webhook_secret\": \"CHANGE_ME_TO_A_STRONG_RANDOM_STRING\"\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","description":"<p>PATTERN — The <code>checkout_url</code> returned by every session is a fully functional, shareable payment link. Distribute it via email, SMS, invoice PDFs, or dashboard buttons.</p>\n<p>No additional API calls or token generation required. Sessions expire after 30 minutes — create a new session if the link has expired.</p>\n","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"14040f6b-6659-9c9c-3dbf-51f35c56d07d"},{"name":"11 · Abandoned Order Recovery — New Session Pattern (Reference)","id":"4a2233d9-3595-6bbc-284d-794926a9e845","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"cb44fd21-f5b4-498a-8cf5-6e8f8319b491"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 89.99,\n  \"currency\": \"USD\",\n  \"wallet\": \"0xYOUR_POLYGON_USDC_WALLET_ADDRESS\",\n  \"order_id\": \"ORD-RECOVERY-EXAMPLE\",\n  \"customer_email\": \"customer@example.com\",\n  \"success_url\": \"https://yourstore.com/order/success\",\n  \"cancel_url\": \"https://yourstore.com/order/cancelled\",\n  \"webhook_url\": \"https://yourstore.com/api/payment-webhook\",\n  \"webhook_secret\": \"CHANGE_ME_TO_A_STRONG_RANDOM_STRING\"\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","description":"<p>PATTERN — Abandoned order recovery for API integrations.</p>\n<p>When a customer creates an order but doesn't complete payment, the session expires after 30 minutes. If no <code>payment.confirmed</code> webhook is received within your recovery window, create a new session for the same order and resend the <code>checkout_url</code> to the customer.</p>\n<p><strong>Suppression guards:</strong></p>\n<ul>\n<li>Order already paid → skip</li>\n<li>Recovery already sent once → skip</li>\n<li>No customer email → skip</li>\n<li>Order older than 72 hours → skip</li>\n</ul>\n","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"4a2233d9-3595-6bbc-284d-794926a9e845"},{"name":"11 · Webhook Recovery — On-Chain Missed Payment Check (Reference)","id":"532bfabb-9b7e-552c-920f-ee6402538972","request":{"method":"GET","header":[],"url":"https://api.polygonscan.com/api?module=account&action=tokentx&contractaddress=0x3c499c542cef5e3811e1192ce70d8cc03d5c3359&address=0xYOUR_POLYGON_USDC_WALLET_ADDRESS&sort=desc&apikey=YOUR_FREE_POLYGONSCAN_KEY","description":"<p>PATTERN REFERENCE — A server-side safety net that confirms payments by reading the Polygon blockchain directly, for cases where a webhook was missed or delayed. This is the direct-API equivalent of the Missed Payment Protection feature built into the VERIFIED WooCommerce plugin.</p>\n<p><strong>Why you need this:</strong> Webhooks are the primary confirmation signal, but no webhook system is perfect — endpoints have downtime, callbacks can be delayed by slow provider KYC, and some payment routes confirm on-chain without an immediate callback. This recovery check guarantees you can confirm any settled payment independently, because USDC settlement on Polygon is public and verifiable.</p>\n<p><strong>Core idea:</strong> Periodically scan your own merchant wallet for incoming USDC transfers that match the expected amount of any order still marked pending. If a match is found, flag the order for confirmation.</p>\n<hr />\n<h2 id=\"design-principles-ported-from-the-production-plugin\">Design principles (ported from the production plugin)</h2>\n<ol>\n<li><strong>Check your OWN merchant wallet.</strong> Funds always land in your destination wallet regardless of how they were routed, so this is the most reliable address to monitor.</li>\n<li><strong>Two query methods, no API key required to start.</strong> Use the public Polygon RPC (<code>eth_getLogs</code>) by default — works with zero setup. Optionally use the Polygonscan API with a free key for higher-volume reliability.</li>\n<li><strong>Match by amount with tolerance.</strong> On-chain amounts rarely match the order total exactly (conversion, rounding). Use a small tolerance (~2%).</li>\n<li><strong>Only check aged, still-pending orders.</strong> Wait at least ~2 hours after order creation so normal webhooks arrive first, then scan on a schedule (e.g. every 3 hours).</li>\n<li><strong>Flag for review — never auto-fulfill blindly.</strong> A matching transfer is strong evidence, but treat it as \"confirm and review,\" not silent auto-completion, to protect against amount mismatches or unrelated deposits.</li>\n</ol>\n<hr />\n<h2 id=\"usdc-contract--transfer-topic-polygon\">USDC contract + Transfer topic (Polygon)</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>USDC (native):  0x3c499c542cef5e3811e1192ce70d8cc03d5c3359\nTransfer topic: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\nPublic RPC:     https://polygon-rpc.com\nDecimals:       6   (so $70.00 = 70000000 raw units)\n</code></pre><hr />\n<h2 id=\"method-a--public-rpc-no-api-key-works-out-of-the-box\">Method A — Public RPC (no API key, works out of the box)</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">const USDC = '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359';\nconst TRANSFER_TOPIC =\n  '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';\n\nasync function rpc(method, params) {\n  const res = await fetch('https://polygon-rpc.com', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),\n  });\n  const json = await res.json();\n  if (json.error) throw new Error(JSON.stringify(json.error));\n  return json.result;\n}\n\n// Find incoming USDC transfers to your wallet since a given block.\nasync function findIncomingUSDC(merchantWallet, fromBlock) {\n  const toBlock = await rpc('eth_blockNumber', []);\n  const paddedTo =\n    '0x000000000000000000000000' + merchantWallet.toLowerCase().replace(/^0x/, '');\n\n  const logs = await rpc('eth_getLogs', [{\n    fromBlock: '0x' + Math.max(1, fromBlock).toString(16),\n    toBlock,\n    address: USDC,\n    topics: [TRANSFER_TOPIC, null, paddedTo], // [event, from(any), to(you)]\n  }]);\n\n  return logs.map(log =&gt; ({\n    tx_hash: log.transactionHash,\n    amount: parseInt(log.data, 16) / 1_000_000,        // 6 decimals\n    block: parseInt(log.blockNumber, 16),\n  }));\n}\n</code></pre>\n<hr />\n<h2 id=\"method-b--polygonscan-api-optional-free-key-higher-reliability\">Method B — Polygonscan API (optional, free key, higher reliability)</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">async function findIncomingUSDC_Polygonscan(merchantWallet, fromBlock, apiKey) {\n  const url = new URL('https://api.polygonscan.com/api');\n  url.searchParams.set('module', 'account');\n  url.searchParams.set('action', 'tokentx');\n  url.searchParams.set('contractaddress', USDC);\n  url.searchParams.set('address', merchantWallet);\n  url.searchParams.set('startblock', String(Math.max(1, fromBlock)));\n  url.searchParams.set('sort', 'asc');\n  url.searchParams.set('apikey', apiKey);\n\n  const res = await fetch(url);\n  const json = await res.json();\n  if (!Array.isArray(json.result)) return [];\n\n  return json.result\n    .filter(r =&gt; (r.to || '').toLowerCase() === merchantWallet.toLowerCase())\n    .map(r =&gt; ({\n      tx_hash: r.hash,\n      amount: Number(r.value) / 1_000_000,\n      block: Number(r.blockNumber),\n    }));\n}\n</code></pre>\n<hr />\n<h2 id=\"amount-matching-with-tolerance\">Amount matching with tolerance</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">function pickMatch(events, expectedAmount) {\n  if (!events.length) return null;\n  const tolerance = Math.max(0.01, expectedAmount * 0.02); // 2%\n  let best = null, bestDiff = null;\n  for (const e of events) {\n    const diff = Math.abs(e.amount - expectedAmount);\n    if (diff &lt;= tolerance &amp;&amp; (bestDiff === null || diff &lt; bestDiff)) {\n      best = e; bestDiff = diff;\n    }\n  }\n  return best;\n}\n</code></pre>\n<hr />\n<h2 id=\"the-scheduled-recovery-scan-run-every-3-hours\">The scheduled recovery scan (run every ~3 hours)</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">const POLYGON_BLOCK_SECONDS = 2;\n\nasync function runRecoveryScan() {\n  // Orders still pending and older than the min age (e.g. 2 hours)\n  const minAgeMs = 2 * 60 * 60 * 1000;\n  const stuck = await db.orders.find({\n    status: 'pending',\n    created_at: { $lt: new Date(Date.now() - minAgeMs) },\n    recovery_checked: { $ne: true },\n  });\n\n  const currentBlockHex = await rpc('eth_blockNumber', []);\n  const currentBlock = parseInt(currentBlockHex, 16);\n\n  for (const order of stuck) {\n    // Narrow the block range from order age (+buffer)\n    const ageSec = Math.floor((Date.now() - new Date(order.created_at)) / 1000);\n    const fromBlock = Math.max(\n      1,\n      currentBlock - Math.ceil(ageSec / POLYGON_BLOCK_SECONDS) - 5000\n    );\n\n    let events;\n    try {\n      events = process.env.POLYGONSCAN_API_KEY\n        ? await findIncomingUSDC_Polygonscan(\n            process.env.MERCHANT_WALLET, fromBlock, process.env.POLYGONSCAN_API_KEY)\n        : await findIncomingUSDC(process.env.MERCHANT_WALLET, fromBlock);\n    } catch (e) {\n      // Retry up to 3 times across future scans, then give up gracefully\n      const retries = (order.recovery_retries || 0) + 1;\n      await db.orders.update({ id: order.id }, { recovery_retries: retries });\n      if (retries &gt;= 3) {\n        await db.orders.update({ id: order.id }, { recovery_checked: true });\n        await alertOps(order, 'Recovery check failed after 3 attempts — review manually');\n      }\n      continue;\n    }\n\n    const match = pickMatch(events, order.expected_amount);\n    await db.orders.update({ id: order.id }, { recovery_checked: true });\n\n    if (match) {\n      // Flag for confirmation — do NOT silently auto-fulfil\n      await db.orders.update({ id: order.id }, {\n        status: 'paid_pending_review',\n        tx_hash: match.tx_hash,\n        on_chain_amount: match.amount,\n        on_chain_block: match.block,\n      });\n      await alertOps(order, `On-chain USDC match found: ${match.amount} USDC, tx ${match.tx_hash}`);\n    }\n  }\n}\n</code></pre>\n<hr />\n<h2 id=\"notes\">Notes</h2>\n<ul>\n<li><strong>No API key path works immediately</strong> via <code>polygon-rpc.com</code>. Add a free Polygonscan key only if you run high volume and want extra reliability.</li>\n<li><strong><code>expected_amount</code></strong> should be the USDC amount you expect to receive. If you pass non-USD currency at session creation it is converted to USD/USDC, so store the converted figure where possible.</li>\n<li><strong>Verify on Polygonscan</strong> any <code>tx_hash</code> at <a href=\"https://polygonscan.com/tx/%7Btx_hash%7D\">https://polygonscan.com/tx/{tx_hash}</a> for an independent audit trail.</li>\n<li>This recovery check complements webhooks — it does not replace them. Keep your signed webhook handler as the primary path and run this scan as the safety net.</li>\n</ul>\n","urlObject":{"protocol":"https","path":["api"],"host":["api","polygonscan","com"],"query":[{"key":"module","value":"account"},{"key":"action","value":"tokentx"},{"description":{"content":"<p>Native USDC on Polygon (Circle-issued). Chain ID 137.</p>\n","type":"text/plain"},"key":"contractaddress","value":"0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"},{"key":"address","value":"0xYOUR_POLYGON_USDC_WALLET_ADDRESS"},{"key":"sort","value":"desc"},{"key":"apikey","value":"YOUR_FREE_POLYGONSCAN_KEY"}],"variable":[]}},"response":[],"_postman_id":"532bfabb-9b7e-552c-920f-ee6402538972"},{"name":"13 · Recurring Payments — Session Per Billing Cycle (Reference)","id":"a8e8549b-fa5a-e3a0-d5a7-1711639ebdb4","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"022473e6-5aaf-49c3-beec-a9e1cebd8c43"}],"body":{"mode":"raw","raw":"{\n  \"amount\": 29.99,\n  \"currency\": \"USD\",\n  \"wallet\": \"0xYOUR_POLYGON_USDC_WALLET_ADDRESS\",\n  \"order_id\": \"RNW-SUB001-74\",\n  \"customer_email\": \"customer@example.com\",\n  \"success_url\": \"https://yourstore.com/order/success\",\n  \"cancel_url\": \"https://yourstore.com/order/cancelled\",\n  \"webhook_url\": \"https://yourstore.com/api/payment-webhook\",\n  \"webhook_secret\": \"CHANGE_ME_TO_A_STRONG_RANDOM_STRING\",\n  \"metadata\": {\n    \"subscription_id\": \"SUB-001\",\n    \"billing_cycle\": \"2026-04\"\n  }\n}","options":{"raw":{"language":"json"}}},"url":"https://payapi.verifiedcryptocheckout.com/v1/session","description":"<p>PATTERN — Recurring payments via session-per-billing-cycle.</p>\n<p>VERIFIED does not manage subscription state. Each billing cycle, your system creates a new payment session for the renewal amount, sends the <code>checkout_url</code> to the customer, and listens for the <code>payment.confirmed</code> webhook. This pattern works with any billing platform.</p>\n<p><strong>Key notes:</strong></p>\n<ul>\n<li>Each renewal is a completely independent session</li>\n<li><code>order_id</code> is required — use a unique value per renewal attempt (include a timestamp)</li>\n<li>You are responsible for tracking subscription state and retry logic in your own system</li>\n</ul>\n","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"a8e8549b-fa5a-e3a0-d5a7-1711639ebdb4"},{"name":"ℹ️ FAQ — Common Integration Questions (Reference)","id":"bc1ce4ec-4dbe-47c9-9a01-2eba2cfb6641","request":{"method":"GET","header":[],"url":"https://payapi.verifiedcryptocheckout.com/v1/session","description":"<p>REFERENCE — Do not send. Answers to common integration questions.</p>\n<h2 id=\"q1--webhook-signing-secret-how-to-obtain-and-rotate\">Q1 — Webhook signing secret: how to obtain and rotate?</h2>\n<p><strong>You define it.</strong> Pass <code>webhook_secret</code> in the session creation body. <strong>It is required whenever <code>webhook_url</code> is set</strong> — an endpoint that verifies signatures will reject (401) an unsigned delivery. VERIFIED uses it to sign outbound webhooks via HMAC-SHA256. To rotate: supply a new value in the next session. Each session carries its own secret independently.</p>\n<h2 id=\"q2--is-there-a-get-endpoint-to-query-session-status\">Q2 — Is there a GET endpoint to query session status?</h2>\n<p><strong>No.</strong> Session state is communicated exclusively via webhook callbacks. If a webhook is missed after 2+ hours, use the Polygonscan on-chain fallback.</p>\n<h2 id=\"q3--webhook-payload-fields\">Q3 — Webhook payload fields</h2>\n<p><strong>9 stable core fields</strong> always present: <code>event</code>, <code>session_id</code>, <code>order_id</code>, <code>amount</code>, <code>currency</code>, <code>wallet</code>, <code>tx_hash</code>, <code>status</code>, <code>timestamp</code>.</p>\n<p><strong>Additional non-breaking fields</strong> also delivered: <code>amount_forwarded</code> (net forwarded after fees), <code>confirmations</code> (on-chain confirmations), <code>raw</code> (full upstream provider passthrough).</p>\n<p>Treat unknown keys as non-breaking and ignore them. Build business logic against the 9 core fields.</p>\n<h2 id=\"q4--event--status-semantics\">Q4 — Event / status semantics</h2>\n<p>Only treat <code>event: payment.confirmed</code> AND <code>status: confirmed</code> as paid. If a <code>payment.pending</code> / <code>status: pending</code> event ever arrives, do not mark the order paid — wait for the <code>confirmed</code> delivery.</p>\n<h2 id=\"q5--multi-chain--multi-token-settlements\">Q5 — Multi-chain / multi-token settlements</h2>\n<p>The webhook fires reliably regardless of settlement chain/token. Payload reflects actual chain and token used. For high-value transactions, verify <code>tx_hash</code> on polygonscan.com.</p>\n<h2 id=\"q6--provider-failover\">Q6 — Provider failover</h2>\n<p>Handled automatically by VERIFIED routing. No API parameter to configure fallback order. Omit <code>provider</code> for automatic routing.</p>\n<h2 id=\"q7--fee-structure\">Q7 — Fee structure</h2>\n<ul>\n<li>Platform fee (4%): deducted from USDC settlement</li>\n<li>Upstream onramp fee (~4%): deducted from card charge before USDC conversion</li>\n<li>Merchant receives USDC net of both</li>\n<li><code>amount_forwarded</code> in the webhook reports the net delivered to your wallet</li>\n</ul>\n<h2 id=\"q8--settlement-timing\">Q8 — Settlement timing</h2>\n<p>~2–5 minutes end-to-end from card approval to webhook fire. Webhook and USDC arrival are simultaneous (same on-chain event).</p>\n<h2 id=\"q9--rate-limits\">Q9 — Rate limits</h2>\n<p>~1 req/sec/IP standard. On 429, back off using <code>Retry-After</code> header. Contact support for enterprise limits.</p>\n","urlObject":{"path":["v1","session"],"host":["https://payapi.verifiedcryptocheckout.com"],"query":[],"variable":[]}},"response":[],"_postman_id":"bc1ce4ec-4dbe-47c9-9a01-2eba2cfb6641"}],"event":[{"listen":"prerequest","script":{"type":"text/javascript","exec":["// Collection-level: validate wallet format before any request","const wallet = pm.collectionVariables.get('merchant_wallet');","if (wallet && wallet !== '0xYOUR_POLYGON_USDC_WALLET_ADDRESS') {","  if (!/^0x[0-9a-fA-F]{40}$/.test(wallet)) {","    console.warn('⚠️  merchant_wallet format invalid. Expected: 0x + 40 hex chars (42 total). Got: ' + wallet);","  }","}"],"id":"8ef38d27-601f-4eb9-89ff-20fe9a3937f9"}}],"variable":[{"key":"base_url","value":"https://payapi.verifiedcryptocheckout.com","description":"VERIFIED API base URL. Do not change."},{"key":"merchant_wallet","value":"0xYOUR_POLYGON_USDC_WALLET_ADDRESS","description":"Your Polygon USDC wallet address. Must start with 0x and be exactly 42 characters (0x + 40 hex). This is your merchant identifier — USDC settles here. USDC sent to an incorrect wallet cannot be recovered."},{"key":"webhook_secret","value":"CHANGE_ME_TO_A_STRONG_RANDOM_STRING","description":"Your webhook signing secret. REQUIRED whenever webhook_url is set. You define this value. VERIFIED uses it to compute HMAC-SHA256 signatures on outbound webhook callbacks. The EXACT same value must be set as VCC_WEBHOOK_SECRET (or equivalent) in your webhook handler's environment so signatures can be verified. An endpoint that verifies signatures will reject (401) an unsigned delivery. Rotate by supplying a new value in the next session create. Never commit to source control."},{"key":"order_id","value":"ORDER-1001","description":"Your unique internal order reference. REQUIRED on every session creation request. Returned in all webhook events for matching. Must be unique per session."},{"key":"customer_email","value":"customer@example.com","description":"Customer email — used by the provider for KYC and transaction confirmation."},{"key":"success_url","value":"https://yourstore.com/order/success","description":"URL to redirect customer after successful payment. Best-effort only — always use webhook as authoritative confirmation."},{"key":"cancel_url","value":"https://yourstore.com/order/cancelled","description":"URL to redirect customer if they cancel or abandon checkout."},{"key":"webhook_url","value":"https://yourstore.com/api/payment-webhook","description":"Your HTTPS endpoint to receive HMAC-SHA256 signed payment confirmation events. Strongly recommended for production. If set, webhook_secret is required."},{"key":"session_id","value":"","description":"Auto-populated by test script after session creation. Store this on your order record to match incoming webhook events."},{"key":"checkout_url","value":"","description":"Auto-populated by test script. Redirect your customer to this URL exactly as returned — do NOT modify, decode, or append parameters."}]}