SpicePay

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

Payment lifecycle A payment moves from requires_payment_method to requires_confirmation to processing to succeeded. From processing it can branch to requires_customer_action and back, or to a terminal failed, cancelled, or expired state. requires_payment_method requires_confirmation processing succeeded requires_customer_action failed / cancelled / expired
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 with id, name, email, phone, phone_country_code (all optional). If the id already 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