PaymentIntent
that includes payment_method_data
and setup_future_usage
.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.
Item | Notes |
---|---|
Secret key | Use server-side only. |
Customer ID | Reuse an existing customer or create one with POST /v1/customers (email recommended). |
Return URL | A page to receive the user after 3-D Secure (return_url ). |
Webhooks | Expose an HTTPS endpoint. Treat payment_intent.succeeded as the authoritative payment-success signal (and that the card is saved to the customer). |
{
"amount": 100,
"currency": "USD",
"confirm": true,
"customer": "cus_1760950869144109056",
"payment_method_data": {
"type": "card",
"card": {
"exp_month": "01",
"exp_year": "2027",
"number": "4111111111111111",
"cvc": "022",
"name": "XX XXX"
}
},
"setup_future_usage": "on_session",
"return_url": "https://yourwebsite.com"
}
status: "succeeded"
(no 3DS).status: "requires_action"
with a next_action
.status: "requires_payment_method"
(invalid card, insufficient funds, etc.).requires_action
){
"id": "pi_XXXX",
"status": "requires_action",
....
"next_action": {
"type": "challenge_redirect",
"challenge_redirect": {
"url": "https://jstest.wooshpay.com/v1/3ds/index.html?...",
"return_url": "https://yourwebsite.com"
}
},
"customer": "cus_1760950869144109056",
"payment_method": "pm_XXXX",
.....
}
next_action.challenge_redirect.url
.return_url
.Optional: You may poll GET /v1/payment_intents/{id}
, but rely on the webhook for final state.
Event | Purpose | What to store |
---|---|---|
payment_intent.succeeded | Authoritative indicator that the payment completed and the card was saved to the customer (because setup_future_usage was set and customer supplied). | Persist (customer_id, payment_method_id) for future charges. You can read event.data.object.payment_method. |
{
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_XXXX",
"status": "succeeded",
....
"customer": "cus_XXXXXXXXXX",
"payment_method": "pm_XXXXXXXXXXXX",
....
}
}
}
{
"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 point | Value |
---|---|
customer | ID from Step 1. |
payment_method | Saved card’s ID from webhook or listing API. |
off_session | false to attempt and on-session charge(user present now)true to attempt an off-session charge (recurring / unsupervised). |
payment_intent.status | Meaning | Your next step |
---|---|---|
requires_payment_method | Card entry/processing failure | Show error; allow new card. |
requires_action | 3-D Secure step-up required | Redirect to next_action.XXXX.XXXX . |
processing | Bank/network is processing | Wait for webhook; avoid duplicate submissions. |
succeeded | Paid and card saved to customer | Persist mapping; fulfill the order. |
Action | Test card number | Notes |
---|---|---|
Successful save + charge | 4111 4111 41111 4111 | Any future date, any CVC |
3-D Secure required | 4462030000000000 | Checkout will prompt for 3DS challenge |
Task | API |
---|---|
Create customer | POST /v1/customers |
Pay & save (Direct API) | POST /v1/payment_intents (with payment_method_data + setup_future_usage ) |
Authoritative success | payment_intent.succeeded |
Charge later | POST /v1/payment_intents (with customer + saved payment_method ) |