WooshPay OpenAPI
Product DocumentAPI ReferenceJS SDK ReferenceSaaS Platform Integration
Product DocumentAPI ReferenceJS SDK ReferenceSaaS Platform Integration
Back to WooshPay Website
  1. Save a customer's payment method without making a payment
  • Online payments
    • Quick Start
    • Integration overview
    • Wooshpay JS SDK
    • Wooshpay Checkout
    • Wooshpay Direct API
    • Payment Link
    • Authorize and capture
    • Build subscriptions integration
    • Testing cards
  • After the payment
    • Webhook
    • Check the webhook signatures
    • 校验webhook签名
  • Add more payment methods
    • Supported payment method
    • Cards
    • Wallets
      • Alipay
      • Alipay HK
      • Apple Pay
      • Google Pay
      • Wechat Pay
      • 微信支付
      • Kakao Pay
      • DANA
      • Boost
      • Grabpay
      • Mcash
      • Touch'n Go
      • ShopeePay
      • UnionPay
      • 9Pay
      • OVO
      • GCash
      • TrueMoney
    • Bank redirects
      • Bancontact
      • BPI
      • Trustly
      • EPS
      • Giropay
      • iDEAL
      • Przelewy24
      • FPX
    • Buy Now Pay Later
      • Klarna
    • Bank Debits
      • Sepa Direct Debit
    • Bank Transfer
      • Bank Transfer in Europe
      • Bank Transfer in United Kingdom
      • Bank Transfer in Indonesia
      • Bank Transfer in Nigeria
      • Bank Transfer in South Africa
    • QR Payments
      • QRIS
      • PromptPay
    • Real-time payments
      • PIX
      • PayNow
      • UPI
      • SPEI
    • Mobile Money
      • Mobile Money - Multi-Country Integration Guide
  • More payment scenarios
    • Save a customer's payment method when they use it for a payment
      • Save payment details during payment with Direct API
      • Save payment method during payment with Drop-in
    • Save a customer's payment method without making a payment
      • Save a payment method with Wooshpay Checkout
      • Save a payment method with Drop-in
      • Save a payment method with Direct API
  • SaaS platform integration
    • Shopify Plugin
    • WooCommerce
    • Shoplazza 店匠
    • Shopastro 星盘
    • Shopline Plugin
    • Sage Connection
  • Payouts
    • Overview
    • Cameroon
    • Europe
    • Ghana
    • Kenya
    • Nigeria
    • Rwanda
    • South Africa
    • Tanzania
    • Uganda
    • United Kindom
    • United States of America
    • Monitor Your Payout Results
  • Resources
    • Supported currencies
  1. Save a customer's payment method without making a payment

Save a payment method with Direct API

This guide shows how to save a customer’s card entirely via your server by creating a SetupIntent with payment_method_data and confirming it in one request. Use this when you can (and are willing to) handle raw card data server-side.
PCI-DSS scope notice
Posting PAN/CVC directly to your servers places you in SAQ D scope. If you want to minimize PCI burden, use Drop-in or Checkout instead.
1.

Prerequisites#

ItemNotes
Secret keyUse your server key only on the backend.
Customer IDReuse or create with POST /v1/customers (email recommended).
Redirect endpointA front-end page to receive the user after 3-D Secure (return_url).
WebhooksExpose an HTTPS endpoint. Treat setup_intent.succeeded as the only authoritative success signal.
1.

Create & confirm a SetupIntent (with card data)#

Create a SetupIntent
Request
{
  "confirm": true,
  "customer": "cus_1838767497487056896",
  "payment_method_types": ["card"],
  "payment_method_data": {
    "type": "card",
    "card": {
      "exp_month": "04",
      "exp_year": "2027",
      "number": "4761344136141390",
      "cvc": "022",
      "name": "XX XXX"
    }
  },
  "usage": "on_session",
  "return_url": "https://yourwebsite.com"
}
Key parameters
FieldRequiredDescription
confirm✔Set true to attempt confirmation immediately.
customer✔The customer to attach the card to.
payment_method_data✔Raw card details (PCI scope).
usage✔on_session (user present now). Future charges can still be off_session.
return_url✔ when 3DS possibleWhere to send the cardholder back after challenge.
Possible responses
Frictionless success: status: "succeeded" → you’ll still confirm via webhook (see §4).
Action required (3-D Secure): status: "requires_action" with next_action.type = "challenge_redirect" .
Failure / try another card: status: "requires_payment_method" plus error details.
Sample “requires_action” response (yours):
{
  "id": "seti_1838873795297804288",
  "object": "setup_intent",
  "status": "requires_action",
  "customer": "cus_1838767497487056896",
  "client_secret": "seti_..._secret_...",
  "next_action": {
    "type": "challenge_redirect",
    "challenge_redirect": {
          "url": "https://XXXXXXXXXXX",
          "return_url": "https://yourwebsite.com"
    }
  },
  "payment_method": "pm_1838873795201335296",
  "payment_method_options": {
    "card": {
         "request_three_d_secure": "auto", 
         "setup_future_usage": "off_session" 
    }
  }
}
Complete 3-D Secure if requires_action
When next_action.type = "challenge_redirect":
1.
Redirect the customer to next_action.challenge_redirect.url.
2.
Cardholder completes the bank challenge.
3.
They are returned to your return_url.
4.
Do not assume success solely from the redirect; rely on the webhook events.
Optional: You may poll GET /v1/setup_intents/{id} on your server, but the source of truth is the webhook event.
1.

Handle webhooks#

Enable these events in the Dashboard and point them to your webhook endpoint:
EventPurposeTypical action
setup_intent.createdSession startsOptional logging / analytics
setup_intent.succeededSaved successfully.Persist (customer_id, payment_method_id) mapping
setup_intent.succeeded****- Payload demo
{
  "id": "evt_1953045921369423872",
  "object": "event",
  "created": 1754477408000,
  "livemode": false,
  "data": {
    "object": {
      "id": "seti_1953034584329289728",
      "object": "setup_intent",
      "created": 1754474705000,
      "livemode": false,
      "status": "succeeded",
      "metadata": {
        "a": "1",
        "b": "2"
      },
      "customer": "cus_1952983283688013824",
      "client_secret": "seti_1953034584329289728_secret_QNWoBNDOZpW4bomjsAgC8DF1",
      "payment_method_types": [
        "card"
      ],
      "payment_method_options": {
        "card": {
          "request_three_d_secure": "auto",
          "setup_future_usage": "off_session"
        }
      },
      "return_url": "https://checkouttest.wooshpay.com/setup/cs_test_1953034583884693504?key=cGtfdGVzdF9OVEUyTWpZeE1ETTNOekExT1RVek16SXdPVFl4T25SRWFEUjRhbWxhVUcxM1RFeG1aMXBGUVdwd2RGVlRaakUyTnpZMU1qZ3pNamswT1RN",
      "payment_method": "pm_1953045864444329984"
    }
  },
  "type": "setup_intent.succeeded"
}
1.

Charge the card later#

Create a PaymentIntent
POST /v1/payment_intents
{
  "amount": 2500,
  "currency": "USD",
  "confirm": true,
  "off_session": false,          // true if no user present
  "customer": "cus_1953770230215868416",
  "payment_method": "pm_123123123",
  "return_url": "https://example.com/pay/complete"
}
Key pointValue
customerID from Step 1.
payment_methodSaved card’s ID from webhook or listing API.
off_sessionfalseto attempt and on-session charge(user present now)true to attempt an off-session charge (recurring / unsupervised).
1.

Test the flow#

ActionTest card numberNotes
Successful save + charge4111 4111 41111 4111Any future date, any CVC
3-D Secure required4462030000000000Checkout will prompt for 3DS challenge
Use your test secret key and point webhooks to your dev endpoint.
Quick reference
TaskAPI
Create sessionPOST /v1/checkout/sessions/setup
Listen to eventssetup_intent.created``setup_intent.succeeded
List cardsGET /v1/customers/{id}/payment_methods
Charge cardPOST /v1/payment_intents (with customer + saved payment_method)
Modified at 2025-08-13 09:58:19
Previous
Save a payment method with Drop-in
Next
Shopify Plugin
Built with