Payments
The payment lifecycle and the three ways to integrate checkout.
A payment in SpicePay represents one purchase intent. Whichever connector processes it, the payment is the stable object your systems track.
Lifecycle
| Status | Meaning |
|---|---|
requires_payment_method |
Payment created, waiting for the customer to choose/enter a method |
requires_confirmation |
Method attached, waiting for confirm |
requires_customer_action |
Customer must complete an action (e.g. 3DS challenge, wallet or provider redirect) |
processing |
With the processor, result pending (crypto payments sit here during confirmations) |
succeeded |
Payment completed — refunds and disputes can now reference it |
failed |
The processor declined or errored; can be retried with a new attempt |
cancelled / expired |
Voided by you, or timed out before completion |
Attaching a customer (optional)
You don't have to create a customer first. Pass customer details on the create call and SpicePay creates the customer record — or reuses the existing one — automatically:
customer_id— attach an existing customer.customer— an object withid,name,email,phone,phone_country_code(all optional). If theidalready exists the record is reused; otherwise it's created with the details you provide.
Either way, the payment shows up on the customer's history and their saved payment methods become available at checkout. See Customers.
Ways to accept a payment
All three integration methods use the same backend API call to create a payment — they differ only in how the customer pays.
1. Hosted Checkout
Create the payment on your server, then redirect the customer to the hosted checkout:
curl -X POST 'https://api.spicepay.net/payments' \
-H 'api-key: YOUR_SECRET_KEY' \
-H 'Content-Type: application/json' \
-d @body.json
{
"amount": 1000,
"currency": "USD",
"profile_id": "YOUR_SHOP_ID",
"payment_link": true,
"customer": {
"email": "ada@example.com",
"name": "Ada Lovelace"
},
"return_url": "https://your-shop.example/return"
}
The response includes the payment_id and merchant_id. Send the customer to:
https://checkout.spicepay.net/pay/{merchant_id}/{payment_id}
The hosted page handles payment input, 3DS, and redirects to your return_url on completion. Checkout branding — logo, colors, typography — is configured per shop in the Control Center.
2. Iframe Embed
Same create call as Hosted Checkout — then embed the checkout inside your own page instead of redirecting:
<iframe
src="https://checkout.spicepay.net/pay/{merchant_id}/{payment_id}"
width="100%"
height="640"
frameborder="0"
allow="payment"
></iframe>
Add the parent origin (e.g. https://your-shop.example) to your shop's Iframe allowed origins list in the Control Center — the checkout page sends a frame-ancestors CSP header derived from it, so embedding from any origin not on the list is blocked by the browser.
3. Build your own checkout
No hosted page, no iframe: create and confirm the payment in one call, passing the payment method data yourself and pinning a specific connector via routing. The call is the same for every provider — only payment_method, payment_method_type, payment_method_data, and the pinned connector change. Pick a connector to see its exact request:
curl -X POST 'https://api.spicepay.net/payments' \
-H 'api-key: YOUR_SECRET_KEY' \
-H 'Content-Type: application/json' \
-d @body.json
{
"amount": 1000,
"currency": "USD",
"profile_id": "YOUR_SHOP_ID",
"confirm": true,
"capture_method": "automatic",
"return_url": "https://your-shop.example/return",
"payment_method": "wallet",
"payment_method_type": "paypal",
"payment_method_data": {
"wallet": {
"paypal_redirect": {}
}
},
"routing": {
"type": "single",
"data": {
"connector": "paypal"
}
}
}
curl -X POST 'https://api.spicepay.net/payments' \
-H 'api-key: YOUR_SECRET_KEY' \
-H 'Content-Type: application/json' \
-d @body.json
{
"amount": 1000,
"currency": "USD",
"profile_id": "YOUR_SHOP_ID",
"confirm": true,
"capture_method": "automatic",
"return_url": "https://your-shop.example/return",
"payment_method": "crypto",
"payment_method_type": "crypto_currency",
"payment_method_data": {
"crypto": {}
},
"routing": {
"type": "single",
"data": {
"connector": "nowpayments"
}
}
}
curl -X POST 'https://api.spicepay.net/payments' \
-H 'api-key: YOUR_SECRET_KEY' \
-H 'Content-Type: application/json' \
-d @body.json
{
"amount": 1000,
"currency": "USD",
"profile_id": "YOUR_SHOP_ID",
"confirm": true,
"capture_method": "automatic",
"return_url": "https://your-shop.example/return",
"payment_method": "crypto",
"payment_method_type": "crypto_currency",
"payment_method_data": {
"crypto": {}
},
"routing": {
"type": "single",
"data": {
"connector": "cryptomus"
}
}
}
For redirect-based methods the response has status requires_customer_action and a next_action.redirect_to_url pointing at the provider's page. Send the shopper there with a top-level navigation (not an iframe — providers like PayPal block framing):
window.location.href = payment.next_action.redirect_to_url;
The provider returns the shopper to your return_url. The selected connector must be enabled on the shop. Crypto works the same way with "payment_method": "crypto" and "payment_method_type": "crypto_currency".
No code: WordPress
Running a WordPress site? The SpicePay plugin adds hosted checkout to your store — products, orders, and refunds are managed in the WordPress admin, with no API calls to write.
Idempotency
Pass your own payment_id when creating a payment — a retried create with the same ID returns the existing payment instead of charging twice.
Related
- Refunds & disputes
- Customers — customer records and saved methods
- Connectors — which processor serves which payment method
- Webhooks — how your systems hear about status changes