PageSourceSearch

https://h2h-docs.modempay.com/assets/js/6f5b1124.971ae51a.js

js modempay.com collected 2026-10-06 21:32:25 UTC 14,352 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkh_2_h_docs=self.webpackChunkh_2_h_docs||[]).push([["875"],{5400(e,n,s){s.r(n),s.d(n,{metadata:()=>t,default:()=>h,frontMatter:()=>d,contentTitle:()=>c,toc:()=>a,assets:()=>o});var t=JSON.parse('{"id":"payment-intents/confirm-charge","title":"Confirm Charge","description":"After you create a direct charge, some networks need one more call from you before the payment can finish. This page is the reference for those calls.","source":"@site/docs/payment-intents/confirm-charge.md","sourceDirName":"payment-intents","slug":"/payment-intents/confirm-charge","permalink":"/docs/payment-intents/confirm-charge","draft":false,"unlisted":false,"editUrl":"https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/docs/payment-intents/confirm-charge.md","tags":[],"version":"current","sidebarPosition":3,"frontMatter":{"sidebar_position":3},"sidebar":"tutorialSidebar","previous":{"title":"Direct Charge","permalink":"/docs/payment-intents/direct-charge"},"next":{"title":"Standard","permalink":"/docs/payment-intents/manual-charge"}}'),r=s(4848),i=s(8453);let d={sidebar_position:3},c="Confirm Charge",o={},a=[{value:"Confirm endpoint",id:"confirm-endpoint",level:2},{value:"Why this endpoint exists",id:"why-this-endpoint-exists",level:3},{value:"Request",id:"request",level:3},{value:"Response",id:"response",level:3},{value:"Redirect payments (no endpoint)",id:"redirect-payments-no-endpoint",level:2},{value:"Finalize endpoint",id:"finalize-endpoint",level:2},{value:"Request",id:"request-1",level:3},{value:"Response",id:"response-1",level:3},{value:"Checking transaction status",id:"checking-transaction-status",level:2},{value:"Response",id:"response-2",level:3},{value:"The fields you will use",id:"the-fields-you-will-use",level:3},{value:"Notes",id:"notes",level:2}];function l(e){let n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"confirm-charge",children:"Confirm Charge"})}),"\n",(0,r.jsx)(n.p,{children:"After you create a direct charge, some networks need one more call from you before the payment can finish. This page is the reference for those calls."}),"\n",(0,r.jsxs)(n.p,{children:["Which call you make depends on the ",(0,r.jsx)(n.code,{children:"next_step"})," in your charge response:"]}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Your charge response shows"}),(0,r.jsx)(n.th,{children:"You call"}),(0,r.jsx)(n.th,{children:"Covered below"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:'auth_mode: "confirm"'}),", ",(0,r.jsx)(n.code,{children:"openExternal: false"})]}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"POST /payments/confirm/:reference"})}),(0,r.jsx)(n.td,{children:"Confirm endpoint"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"openExternal: true"})}),(0,r.jsx)(n.td,{children:"Nothing, the payment completes on its own"}),(0,r.jsx)(n.td,{children:"Redirect payments"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:'auth_mode: "pin"'})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"POST /payments/finalize/:reference"})}),(0,r.jsx)(n.td,{children:"Finalize endpoint"})]})]})]}),"\n",(0,r.jsxs)(n.p,{children:["In every endpoint on this page, ",(0,r.jsx)(n.code,{children:":reference"})," is the ",(0,r.jsx)(n.code,{children:"transactionId"})," from your charge response."]}),"\n",(0,r.jsxs)(n.p,{children:["For the full integration walkthrough, see the ",(0,r.jsx)(n.a,{href:"/docs/direct-charge",children:"Direct Charge guide"}),". This page covers the endpoints in detail."]}),"\n",(0,r.jsx)(n.h2,{id:"confirm-endpoint",children:"Confirm endpoint"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"POST"})," ",(0,r.jsx)(n.code,{children:"/payments/confirm/:reference"})]}),"\n",(0,r.jsxs)(n.p,{children:["Use it when ",(0,r.jsx)(n.code,{children:"auth_mode"})," is ",(0,r.jsx)(n.code,{children:'"confirm"'})," and ",(0,r.jsx)(n.code,{children:"openExternal"})," is ",(0,r.jsx)(n.code,{children:"false"})," (AfriMoney USSD or in-app approval)."]}),"\n",(0,r.jsx)(n.h3,{id:"why-this-endpoint-exists",children:"Why this endpoint exists"}),"\n",(0,r.jsxs)(n.p,{children:["The customer approves the payment on their own phone. Modem Pay checks with the provider on a schedule, so without your help the payment can take ",(0,r.jsx)(n.strong,{children:"up to ~30 minutes"})," to show as ",(0,r.jsx)(n.code,{children:"completed"}),". Calling confirm tells Modem Pay to check ",(0,r.jsx)(n.strong,{children:"now"}),"."]}
1),"\n",(0,r.jsx)(n.p,{children:"That is why the recommended flow is:"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsx)(n.li,{children:"Show the customer the confirmation instructions"}),"\n",(0,r.jsxs)(n.li,{children:["Show an ",(0,r.jsx)(n.strong,{children:'"I have paid"'})," button"]}),"\n",(0,r.jsx)(n.li,{children:"When they tap it, call this endpoint"}),"\n",(0,r.jsx)(n.li,{children:"Then poll the transaction status or wait for the webhook"}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"If the customer never taps the button, the payment still completes on the next scheduled check. Confirm is about speed, not correctness."}),"\n",(0,r.jsx)(n.h3,{id:"request",children:"Request"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:'curl -X POST "https://api.modempay.com/h2h/v1/payments/confirm/cos-212q2mbrg8z6m" \\\n  -H "Authorization: Bearer YOUR_API_KEY" \\\n  -H "Content-Type: application/json"\n'})}),"\n",(0,r.jsx)(n.h3,{id:"response",children:"Response"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n  "message": "ok",\n  "confirmation": "started"\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["This response means the check has ",(0,r.jsx)(n.strong,{children:"started"}),". It does not mean the payment succeeded. Get the final answer from the ",(0,r.jsx)(n.a,{href:"#checking-transaction-status",children:"transaction status"})," or your webhook."]}),"\n",(0,r.jsx)(n.h2,{id:"redirect-payments-no-endpoint",children:"Redirect payments (no endpoint)"}),"\n",(0,r.jsxs)(n.p,{children:["When ",(0,r.jsx)(n.code,{children:"openExternal"})," is ",(0,r.jsx)(n.code,{children:"true"})," (Wave), there is nothing to call:"]}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Send the customer to ",(0,r.jsx)(n.code,{children:"launch_url"})]}),"\n",(0,r.jsx)(n.li,{children:"The customer authorizes on the provider's page or app"}),"\n",(0,r.jsx)(n.li,{children:"Modem Pay detects the result automatically and sends a webhook"}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"Calling confirm or finalize for a redirect payment is not needed and not supported."}),"\n",(0,r.jsx)(n.h2,{id:"finalize-endpoint",children:"Finalize endpoint"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"POST"})," ",(0,r.jsx)(n.code,{children:"/payments/finalize/:reference"})]}),"\n",(0,r.jsxs)(n.p,{children:["Use it when ",(0,r.jsx)(n.code,{children:"auth_mode"})," is ",(0,r.jsx)(n.code,{children:'"pin"'})," (QMoney, APS). The customer gives you their PIN/OTP and you submit it."]}),"\n",(0,r.jsxs)(n.p,{children:["The charge response includes ",(0,r.jsx)(n.code,{children:"auth_length"}),", the number of digits to collect. Use it to size your PIN input instead of hard-coding a length."]}),"\n",(0,r.jsx)(n.h3,{id:"request-1",children:"Request"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:'curl -X POST "https://api.modempay.com/h2h/v1/payments/finalize/cos-212q2mbrg8z6m" \\\n  -H "Authorization: Bearer YOUR_API_KEY" \\\n  -H "Content-Type: application/json" \\\n  -d \'{\n    "pin": "123456"\n  }\'\n'})}),"\n",(0,r.jsx)(n.p,{children:"Never store the PIN. Send it and forget it."}),"\n",(0,r.jsx)(n.h3,{id:"response-1",children:"Response"}),"\n",(0,r.jsxs)(n.p,{children:["Finalize returns the full transaction object (same shape as the status endpoint below). The field to check is ",(0,r.jsx)(n.code,{children:"status"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n  "id": "bb270a1f-e0a4-4a3d-bba6-e4046e09d5d0",\n  "status": "completed",\n  "amount": 100,\n  "currency": "GMD",\n  "payment_method": "qmoney",\n  "failure_reason": null,\n  "...": "..."\n}\n'})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:'status: "completed"'})," \u2192 the payment is done. Deliver value"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:'status: "processing"'})," or ",(0,r.jsx)(n.code,{children:'"pending"'})," \u2192 not final yet. Keep verifying via status or webhook"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:'status: "failed"'})," \u2192 check ",(0,r.jsx)(n.code,{children:"failure_reason"})," and let the customer retry with a new payment"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"checking-transaction-status",children:"Checking transaction status"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"GET"})," ",(0,r.jsx)(n.code,{children:"/transactions/:reference"})]}),"\n",(0,r.jsx)(n.p,{children:"Works for every flow. Use it to update your UI after a confirm call, or as your polling method if you do not use webhooks."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:'curl -X GET "https://api.modempay.com/h2h/v1/transactions/cos-212q2mbrg8z6m" \\\n  -H "Authorization: Bearer YOUR_API_KEY"\n'})}),"\n",(0,r.jsx)(n.h3,{id:"response-2",children:"Response"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n  "id": "bb270a1f-e0a4-4a3d-bba6-e4046e09d5d0",\n  "payment_intent_id": "05bf5898-de54-480c-b822-64c18e3c1a03",\n  "amount": 100,\n  "currency": "GMD",\n  "status": "processing",\n  "reference": "RVMGQVWLU",\n  "transaction_reference": "RVMGQVWLU",\n  "payment_method": "afrimoney",\n  "payment_account": ".... 4725",\n  "failure_reason": null,\n  "transaction_fee": 0,\n  "transaction_fee_type": "business",\n  "test_mode": false,\n  "paid_date": null,\n  "requires_auth": true,\n  "auth_mode": "confirm",\n  "auth_length": 0,\n  "openExternal": false,\n  "launch_url": "",\n  "metadata": {},\n  "payment_metadata": {},\n  "customer_name": null,\n  "customer_phone": null,\n  "customer_email": null,\n  "business_id": "120c298f-b736-49c1-8220-8a8c84a5d8f3",\n  "account_id": "199bf17b-0b5a-45c0-a5b0-1d29f5f173c9",\n  "createdAt": "2025-11-10T09:32:55.769Z",\n  "updatedAt": "2025-11-10T09:32:58.191Z"\n}\n'})}),"\n",(0,r.jsx)(n.h3,{id:"the-fields-you-will-use",children:"The fields you will use"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Field"}),(0,r.jsx)(n.th,{children:"Meaning"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"status"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"pending"}),", ",(0,r.jsx)(n.code,{children:"processing"}),", ",(0,r.jsx)(n.code,{children:"completed"}),", ",(0,r.jsx)(n.code,{children:"failed"}),", ",(0,r.jsx)(n.code,{children:"cancelled"}),", or ",(0,r.jsx)(n.code,{children:"abandoned"}),". Only ",(0,r.jsx)(n.code,{children:"completed"})," is success. See ",(0,r.jsx)(n.a,{href:"/docs/direct-charge#payment-statuses",children:"payment statuses"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"failure_reason"})}),(0,r.jsx)(n.td,{children:"Why the payment failed. Show it (or a friendly version of it) to the customer"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"paid_date"})}),(0,r.jsx)(n.td,{children:"When the payment completed"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"amount"}),", ",(0,r.jsx)(n.code,{children:"currency"}),", ",(0,r.jsx)(n.code,{children:"transaction_fee"})]}),(0,r.jsx)(n.td,{children:"The money details for this payment"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"payment_account"})}),(0,r.jsx)(n.td,{children:"Masked customer account, safe to show on receipts"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"test_mode"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"true"})," means this is a test payment. Never deliver real value for test payments"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"metadata"})}),(0,r.jsx)(n.td,{children:"The key-value pairs you sent when creating the payment"})]})]})]}),"\n",(0,r.jsxs)(n.p,{children:["The remaining fields (",(0,r.jsx)(n.code,{children:"business_id"}),", ",(0,r.jsx)(n.code,{children:"account_id"}),", ",(0,r.jsx)(n.code,{children:"payment_metadata"}),", and so on) are for records and reconciliation. You do not need them to complete a payment."]}),"\n",(0,r.jsx)(n.h2,{id:"notes",children:"Notes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:":reference"})," is always the ",(0,r.jsx)(n.code,{children:"transactionId"})," from your charge response"]}),"\n",(0,r.jsxs)(n.li,{children:["Confirm and finalize make payments complete ",(0,r.jsx)(n.strong,{children:"faster"}),". The final truth is always the transaction ",(0,r.jsx)(n.code,{children:"status"})]}),"\n",(0,r.jsx)(n.li,{children:"Webhooks send you the same status updates without polling, use them if you can"}),"\n"]})]})}function h(e={}){let{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(l,{...e})}):l(e)}},8453(e,n,s){s.d(n,{R:()=>d,x:()=>c});var t=s(6540);let r={},i=t.createContext(r);function d(e){let n=t.useContext(i);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:d(e.components),t.createElement(i.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.