Integrations

Drop-in plugins and hosted checkout flows.

Web SDK (spuke.js)

Drop-in JavaScript library — like Stripe.js. Redirect customers to hosted checkout from any website.

Overview

spuke.js is a lightweight (~5 KB) browser library. You load it from our CDN, initialise it with a Publishable Key (pk_), and call .checkout() to redirect the customer to the hosted checkout on payments.spuke.com.

No server code required. Works with plain HTML, React, Vue, Svelte, or any framework.

1. Create a Publishable Key

Go to Dashboard → Developers, click New API key, choose Publishable (pk_), and whitelist the domains that are allowed to use it (e.g. mystore.com, *.mystore.com, localhost).

Publishable keys are safe to expose in your frontend — they can only start checkouts for prices that already exist in your catalogue.

2. Add the script

<script src="https://payments.spuke.com/spuke.js"></script>

3. Start a checkout

<button id="buy">Pay 25.00 EUR</button>
<script>
  document.getElementById('buy').addEventListener('click', () => {
    spuke('pk_live_XXX').checkout({
      line_items: [{ price: 'price_XXXXXXXX', quantity: 1 }],
      customer_email: 'customer@example.com',
      customer_name: 'Jane Doe',
      success_url: window.location.origin + '/thanks',
      cancel_url:  window.location.href,
    });
  });
</script>

The customer is redirected to https://payments.spuke.com/c/… where they complete the payment. On success they land on your success_url with ?session_id=cs_… appended.

React example

import { useEffect } from "react";

export function BuyButton({ pk, priceId }: { pk: string; priceId: string }) {
  useEffect(() => {
    const s = document.createElement("script");
    s.src = "https://payments.spuke.com/spuke.js";
    s.async = true;
    document.body.appendChild(s);
    return () => { s.remove(); };
  }, []);

  return (
    <button onClick={() => (window as any).spuke(pk).checkout({
      line_items: [{ price: priceId, quantity: 1 }],
      customer_email: "customer@example.com",
      customer_name: "Jane Doe",
    })}>Pay</button>
  );
}

API reference

spuke(publishableKey)

Returns a client instance. Cache it if you use it multiple times.

.checkout(params)

Field Required Description
line_items yes Array of { price: 'price_…', quantity: number }. Prices must exist in your catalogue.
customer_email yes End-customer email. Used for the receipt.
customer_name yes End-customer full name.
success_url no URL to redirect to after payment. Defaults to your Checkout Builder setting.
cancel_url no URL to redirect to if the customer aborts.
metadata no Object of string key/value pairs stored on the transaction.

Returns a Promise that resolves once the redirect is issued.

Security

  • Publishable keys only accept requests from your whitelisted origins.
  • They cannot create refunds, list transactions, or read customer data.
  • They cannot set arbitrary amounts — every line item must reference a price_… in your catalogue.
  • For server-side actions (refunds, listing payments, webhooks) use a Secret Key (sk_) from your backend.

Testing

Create a key in Test mode from Developers. Test-mode keys route to Stripe test cards (4242 4242 4242 4242).

Hosted Checkout

Redirect to payments.spuke.com/c/{id} or embed payments.spuke.com/embed/{id} — you own no PCI scope.

Hosted Checkout

spuke ships two URLs for the hosted checkout — one for real payments, one for design preview. Do not mix them up.

Route Purpose Real payment?
/c/{session_id} Real hosted checkout for a session created via API ✅ Yes
/embed/{session_id} Same as above, optimised for iframe embedding ✅ Yes
/c/s/{slug} Preview only — renders your Checkout Builder branding with a dummy product. Ignores ?session=…. ❌ No

1. Create a session (server-side)

curl -X POST https://api.spuke.com/v1/checkout/sessions \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4990,
    "currency": "CHF",
    "line_items": [{ "name": "T-Shirt", "quantity": 1, "amount": 4990 }],
    "success_url": "https://your-shop.com/thanks",
    "cancel_url":  "https://your-shop.com/cart"
  }'

Response:

{
  "id": "cs_01H…",
  "checkout_url": "https://payments.spuke.com/c/cs_01H…",
  "embed_url":    "https://payments.spuke.com/embed/cs_01H…"
}

2. Send the buyer to the real checkout

Redirect

window.location = session.checkout_url;   // -> /c/{session_id}

Iframe

<iframe src="{{embed_url}}"
        style="width:100%;height:720px;border:0"
        allow="payment *"></iframe>

Web component

<script src="https://payments.spuke.com/spuke.js"></script>
<spuke-checkout session-id="cs_01H…"></spuke-checkout>

The slug in the snippets from Dashboard → Checkout builder only tells spuke which branding profile to apply — the session_id is what makes it a real transaction. Without a valid session, the page falls back to preview mode with dummy data.

3. What the buyer sees

  • Merchant logo, business name and support email (from Checkout builder).
  • Line items with product images (line_items[].image_url).
  • All payment methods enabled for the currency in your plan + Stripe.
  • 3-D Secure / SCA when required.
  • Success page with spuke-hosted receipt (/r/{merchant_id}/{tx_id}) + PDF.

Preview mode (/c/s/{slug})

Use this URL in your browser to see how your checkout looks with the current Builder settings. It renders a hard-coded sample product for CHF 49.90 and a static list of payment methods. Any ?session=… parameter is ignored — this route never talks to Stripe and never charges anyone.

To test a real payment end-to-end, always create a session via the API first and open /c/{session_id}.

Success & cancel URLs

Optional. When set, spuke redirects the buyer after the session ends and appends ?session_id={id} so you can look up the result.

Customising

Dashboard → Checkout builder controls colours, logo, business name, support email and return URL. Changes apply to every future session — no code changes needed.

WooCommerce plugin

Drop-in payment gateway for WordPress

WooCommerce plugin

The plugin adds spuke as a payment gateway in WooCommerce. It works on classic checkout and Cart/Checkout Blocks, handles both live and test modes, and receives webhooks to reconcile orders even if the buyer closes the tab.

Current version: 1.4.2 — download it from Dashboard → Integrations → WooCommerce.

Requirements

  • WordPress 6.0+, WooCommerce 6.0+
  • PHP 7.4+
  • Non-plain permalinks (needed for the WC REST API)

Install

  1. Dashboard → Integrations → WooCommerce → Download plugin — the ZIP is generated on the fly.
  2. WP Admin → Plugins → Add New → Upload Plugin → pick spuke-woocommerce-1.4.2.zipInstallActivate.
  3. WooCommerce → Settings → Payments → enable spuke.

Configure

Open the spuke gateway settings and fill in:

Field What to paste
Live API key An sk_live_... key from Dashboard → Developers → API keys.
Test API key An sk_test_... key.
Mode Live in production, Test while integrating.
Webhook signing secret The whsec_... shown when you create the webhook endpoint (next step).

Register the webhook

In Dashboard → Developers → Webhooks → Add endpoint set:

  • URL: https://your-store.com/wc-api/spuke
  • Events: checkout.session.completed, payment.succeeded, payment.failed, charge.refunded

Copy the signing secret into the plugin's Webhook signing secret field. Without it the plugin rejects every incoming call with 401 invalid signature and orders stay pending.

Order flow

  1. Customer places order → WooCommerce calls POST /v1/checkout/sessions with the cart total, currency, and metadata.wc_order_id.
  2. Customer is redirected to checkout_url (spuke hosted checkout).
  3. On success:
    • spuke redirects the buyer to WooCommerce's return URL.
    • spuke sends payment.succeeded to /wc-api/spuke.
    • Plugin verifies the signature (5-minute replay tolerance), matches the order by metadata.wc_order_id, and calls payment_complete().
  4. If the buyer closes the tab, the webhook still completes the order — the redirect is not the source of truth.

Idempotency

The plugin stores the last 50 event.ids per order in post meta (_spuke_events_seen). Duplicate deliveries are skipped without side effects.

Testing

Switch the plugin to Test, place a real order, pay with 4242 4242 4242 4242 (any future expiry, any CVC). The order should move to Processing within a few seconds of the redirect.

Troubleshooting

Symptom Fix
Gateway not visible at checkout Confirm the plugin is activated and toggled on in WC → Settings → Payments.
401 invalid signature in server log Signing secret in plugin ≠ secret shown in dashboard. Regenerate the endpoint and re-paste.
Order stuck at pending after redirect Webhook URL wrong or blocked by firewall. Test with curl -X POST https://your-store.com/wc-api/spuke -d '{}' — must return 401 (signature missing), not 404.
rest_no_route in WP debug log Permalinks are set to Plain. Switch to Post name in WP → Settings → Permalinks.

PrestaShop module

Native payment module for PrestaShop 1.7 & 8.x

PrestaShop module

The spukepay module registers spuke as a payment option in PrestaShop. It supports PrestaShop 1.7.6+ and 8.0 – 8.99, works with guest and account checkout, forwards billing & shipping data, product images, and reconciles orders via signed webhooks — even when the buyer closes the tab.

Current version: 1.0.5 — download it from Dashboard → Integrations → PrestaShop.

Requirements

  • PrestaShop 1.7.6 – 8.99
  • PHP 7.4+ (8.x recommended)
  • Friendly URLs enabled (needed for controller routes)
  • Outbound HTTPS to api.spuke.com

Install

  1. Dashboard → Integrations → PrestaShop → Download module — a fresh spukepay-1.0.5.zip is generated for your account.
  2. Back Office → Modules → Module Manager → Upload a module → pick the ZIP.
  3. After install, click Configure on the spuke tile.

Configure

Field What to paste
Live API key An sk_live_... key from Dashboard → Developers → API keys.
Test API key An sk_test_... key.
Mode Live in production, Test while integrating.
Webhook signing secret The whsec_... shown when you create the webhook endpoint below.
Display name Label shown to customers at checkout (default: spuke).

Register the webhook

In Dashboard → Developers → Webhooks → Add endpoint set:

  • URL: https://your-store.com/module/spukepay/webhook
  • Events: checkout.session.completed, payment.succeeded, payment.failed, charge.refunded, charge.dispute.created

Copy the signing secret into the module's Webhook signing secret field. Without it every incoming call is rejected with 401 invalid signature and orders stay in Awaiting payment.

Order flow

  1. Customer selects spuke on the payment step → module calls POST /v1/checkout/sessions with cart total, currency, line items (name + image + qty), billing/shipping address, phone, email and metadata.ps_cart_id.
  2. Session state (cart id, secure key, customer id) is persisted in the dedicated {PREFIX}spukepay_session table — no misuse of the global configuration table.
  3. Customer is redirected to spuke Hosted Checkout.
  4. On success:
    • Buyer is sent to /module/spukepay/validate which auto-refreshes every 6 seconds until the webhook lands.
    • spuke sends payment.succeeded to /module/spukepay/webhook.
    • Module verifies HMAC (5-minute replay window), matches the cart via metadata.ps_cart_id, promotes an existing Preparation order to Paid and attaches an OrderPayment.
  5. Terminal states (Paid, Shipped, Delivered) are protected — late expired or canceled events cannot downgrade them.

Refunds

Create a credit slip in the PS back office (Orders → click order → Partial refund / Standard refund). The module hooks actionOrderSlipAdd and calls POST /api-refunds with the exact slip amount (proportional per line item, matching the original discount distribution).

Discounts, rounding & multi-currency

  • Cart-level discounts are distributed proportionally across line items so Stripe never receives negative amounts.
  • Rounding drift between PrestaShop's cart total and the summed line items is absorbed on the largest line item (max ±1 minor unit).
  • Zero-decimal currencies (JPY, KRW, HUF, etc.) are scaled correctly; three-decimal currencies (BHD, JOD, KWD, OMR, TND) send the fractional minor units.

Idempotency

The module stores the last 50 processed event.ids per cart in the spukepay_session row. Duplicate deliveries are dropped without side effects. The Idempotency-Key on create is the PS cart id — retries never create a second Stripe session.

Testing

Switch the module to Test, place a real order, pay with 4242 4242 4242 4242 (any future expiry, any CVC). The order should move to Paid within a few seconds of the redirect. Use 4000 0000 0000 9995 to trigger payment.failed.

Troubleshooting

Symptom Fix
Payment could not be initialised. Please try again. The API key is wrong for the selected mode, or the store cannot reach api.spuke.com. Check Back Office → Advanced Parameters → Logs.
Module missing at checkout Reset the module in Module Manager and check that the currency is enabled in Payment → Preferences → Currency restrictions.
401 invalid signature in server log Signing secret in module ≠ secret shown in dashboard. Regenerate the endpoint and re-paste.
Order stuck at Awaiting payment Webhook URL wrong or blocked. curl -X POST https://your-store.com/module/spukepay/webhook -d '{}' must return 401 (signature missing), not 404.
Huge logo on payment step Older versions shipped the raw logo. Update to 1.0.2+ — the logo is normalised to 124 px width.
Product images missing in the spuke dashboard Update to 1.0.5+ — earlier versions sent the wrong JSON key.
Chrome asks about "access to other apps and services" That is the Chrome Payment Handler prompt for Google Pay / Link on a first-seen domain. One-time "Allow" — Chrome remembers it per origin.

Uninstall

Module Manager → spuke → Uninstall. This drops the spukepay_session table and removes all configuration entries. Orders and credit slips are preserved.