Skip to main content

Overview

This guide covers the core card payment integration end to end: registering a user, collecting their card through the Push Widget, authorizing a transaction, handling step-up authentication when the authorization engine requires it, and retrieving the final result. The widget renders and tokenizes the card, so your integration is limited to generating a URL, tokenizing through the widget, and authorizing against the Push API. Process a new card Once a user has paid, their card is stored as a reusable credential. Returning users can pay with a previously-stored credential instead of re-entering card details — see Stored credentials. The same flow also pays users out: to process a withdrawal (cash_out), see Withdrawals.

Integration overview

The steps below show an overview of how to process a card payment.
  1. Register the user. Call the create-user endpoint with the user’s identity information (name, email, address, and phone number).
    • Store the returned Push id alongside your internal user record.
    • Register each user only once and reuse the id on every subsequent transaction. Provide your internal user identifier in the tag field to ensure idempotency.
  2. Display the widget. Call the create-user-url endpoint with the user’s id and payment direction, then render the widget to your payment page using the returned url.
    • The widget automatically validates the card and invokes the onValid callback when the form is complete — enable your submit button there.
    • For a returning user, you can skip the widget and let them pay with a previously-stored credential instead. See Stored credentials.
  3. Authorize the transaction. When the user submits, generate a token from the widget (or use a stored credential_id) and call the authorize-payment endpoint with the payment details (amount, currency, direction), the token, and a redirect_url. Handle the response by its HTTP status code:
    • 202 Accepted — the user must complete authentication. Persist the returned intent id, then continue to step 4 using the returned url.
    • 200 OK — the payment was approved immediately. Notify the user that the payment succeeded.
    • 401 Unauthorized — the payment was declined.
    If your organization is assigned multiple settlement accounts, include the relevant account_id on every authorization request — retrieve the list with list-accounts. Omitting account_id while multiple accounts exist will fail the request. Organizations with a single settlement account can ignore this.
  4. Direct the user to complete authentication. Navigate the user to the url returned from the authorize call. When the flow completes, the user is returned to your application via the redirect_url you set on the authorize request. Present the authentication UI in an iframe or the device’s system browser depending on your environment — see Presenting the authentication UI.
  5. Retrieve the result. Call the get-an-intent endpoint and inspect the status field.
    • If status is approved, update your internal transaction record and notify the user the payment succeeded.
    • If status is declined, the payment could not be approved (e.g. insufficient_funds).

Widget

The Push SDK exposes a Widget that renders the card form and tokenizes the card. Instantiate it with the url returned by create-user-url, rendering it into an element on your payment page. The widget validates the card as the user types and invokes onValid when the form is complete; enable your submit button there.
When the user submits, call widget.tokenize() to generate a token, then send it to your backend to authorize the payment.
For full widget customization (colors, padding, typography), see the JS SDK Reference.

Stored credentials

After a user’s first payment, their card is stored as a reusable credential. A returning user can pay without re-entering card details — skip the widget and let them select a previously-stored credential instead. Process using a stored card
  1. List and display the stored credentials. Call the list-user-credentials endpoint with the user’s id and the type values for the payment. For deposits, pass secure_debit, card_only_credit, and card_only_debit as the allowed types; for withdrawals, pass secure_debit. Display each returned credential’s card_brand and card_last4 and allow the user to select which card to use.
  2. Authorize with the credential_id. Call authorize-payment with the selected credential_id (instead of a widget token), the payment details, and a redirect_url. Handle the response by its HTTP status code exactly as in the Integration overview.
  3. Direct the user to complete authentication. If authorization returns 202 Accepted, navigate the user to the returned url — authentication may be requested even for a stored credential. See Presenting the authentication UI.
  4. Retrieve the result. Call the get-an-intent endpoint and inspect status to confirm the payment was approved or declined.
Authorize With Credential ID

Withdrawals

The steps above collect a payment (cash_in). To pay a user out, follow the same five-step flow with direction: cash_out and the differences below.
  1. Restrict the widget to debit cards. Withdrawals settle to a bank account, so they require a secure_debit credential — a debit card with a linked account. When collecting a new card for a withdrawal, pass type: secure_debit to create-user-url so the widget only accepts debit cards. For a returning user, list stored credentials with ?type=secure_debit before presenting the selection UI (see Stored credentials).
  2. Authorize with direction: cash_out. Call authorize-payment with direction: cash_out. Optionally set approval_mode: manual to hold the approved withdrawal for review instead of posting it to the network automatically — see Reviewing withdrawals.
  3. Direct the user to complete authentication. Identical to a deposit — when the authorization returns 202 Accepted, navigate the user to the returned url. See Presenting the authentication UI.
  4. Retrieve the result. Call get-an-intent and inspect status. A withdrawal authorized with approval_mode: manual returns status: pending — Push has approved it, but it is not posted to the network until you approve it.
  5. (Optional) Approve the payment. If you authorized with approval_mode: manual, approve the pending intent to post it to the network — see Reviewing withdrawals.
Create User URL (Withdrawal)

Reviewing withdrawals

Setting approval_mode: manual on a cash_out authorization holds the approved withdrawal in a pending state so your team can review it before funds are posted to the network. Review the intent with get-an-intent, then either approve it to post the payment or cancel it.
Automatic posting can only be disabled for cash_out payments. Always approve or cancel manual withdrawals so every intent reaches a terminal status and is not left pending indefinitely.
Authorize Withdrawal (Manual Approval)
Approve Pending Intent
Cancel Pending Intent

Refunds

Return funds to a user by refunding an approved cash_in intent.
  1. Submit the refund. Call the create-refund endpoint with the intent_id of the payment you want to refund.
    • Optionally include an amount to refund a portion of the intent. If amount is omitted, the full intent amount is refunded.
    • Only one refund is allowed per intent — subsequent refund attempts on the same intent return a 200 with the already-created refund.
    To check whether an intent has already been refunded, call get-an-intent and inspect the refund field on the response.
    A refund can be declined by the issuer if the user’s bank rejects the transaction.
  2. Handle the response. Check the HTTP status code:
    • 200 OK — the refund was created.
    • 401 Unauthorized — the refund was declined by the payment network. The response body includes the id of the declined refund.
    • 400 Bad Request — a validation error (e.g. the refund amount is greater than the intent amount).
Create Refund (full amount)
Create Refund (partial amount)

Disputes

A dispute records a completed payment that the payer’s bank or card issuer is attempting to reverse. When a cardholder charges back a card payment, or a receiving bank returns an ACH entry, Push opens a dispute against the original intent so you have a single object to track the challenge and its outcome. Each dispute is tied to exactly one intent, and it also appears on the Ledger as a dispute-type transaction that reverses the original funds. Push opens a dispute from two sources, in both cases taking the disputed amount and a network reason code from the reversal:
  • Card chargebacks — a cardholder or issuer disputes a card payment. The code is the card-network reason code, for example 4853 (Cardholder Dispute) or 13.1 (Merchandise or Services Not Received).
  • ACH returns — the receiving bank returns an ACH entry rather than honoring it. The code is the ACH return code, for example R01 (Insufficient Funds) or R02 (Account Closed).
A dispute opens in the created state and stays there while the challenge is worked. Card chargebacks can be contested through representment — Push submits evidence to the issuer on your behalf — and ACH returns may be retried. Push manages that process; the dispute stays created until the network reaches a decision, at which point it moves to a terminal state. Track disputes through both a push and a pull path:
  • dispute.created webhook (push). When a dispute is opened against one of your intents, Push sends a signed dispute.created event to the webhook endpoint you configured on the originating payment — the signal to listen for so you learn about a chargeback or return without polling.
  • Dispute API (pull). Fetch a single dispute with get-a-dispute, or query them with list-disputes filtering by status, code, type, and creation time. The current dispute is also embedded as the dispute field on the get-an-intent response.

Presenting the authentication UI

When authorize-payment returns 202 Accepted, present the authentication UI hosted at the returned url. Choose the presentation based on how the user accesses your application: User authentication
  • Web (mobile or desktop): use the Push JS SDK’s UX to present the Push-hosted UI in a modal, and omit redirect_url from the authorization request. It fires your onExit callback when the flow ends.
  • Native mobile app: open the authentication URL in the device’s default system browser (Safari on iOS, Chrome on Android). Set redirect_url on the authorization request to bring the user back to your app when authentication completes.
Use a custom scheme (e.g. pushcash://) or a Universal Link / App Link as your redirect_url so the system browser can hand control back to your app. On the web, pass the intent id from the 202 response to UX and open the modal. onExit fires once when the flow ends — the user either completes or dismisses it — at which point you confirm the outcome with get-an-intent:

Sandbox testing

Work through the following against the sandbox host (sandbox.pushcash.com) before requesting production access:
  • Register each user only once and reuse their id on subsequent transactions
  • Simulate an approved transaction using test card 5555 5555 5555 4444
  • Simulate a declined transaction using test card 5999 9919 6976 9266
  • Test an authentication-required (202 Accepted) response using test card 6011 0009 9013 9424, and confirm you persist the intent id and present the authentication UI at url
  • Test a payment decline after the user completes authentication using test card 5999 9819 6976 9283
  • Add a new card, then submit a second transaction using the stored credential_id
  • Verify that only secure_debit credentials are presented for withdrawals
  • Process a withdrawal (cash_out) with a secure_debit card, then authorize with approval_mode: manual and approve the pending intent
  • Refund an approved intent, and test a partial refund by specifying an amount less than the original intent amount
  • Test a refund decline by processing an intent with test card 5999 9619 6976 9301 and then submitting a refund

Next steps

Now that you can process card payments, set up webhooks to receive asynchronous updates on the final payment result — see the enabling webhooks guide.