SETUP GUIDES · PAYMENTS

Receiving payment events in your app

Let Claspine tell your app when a customer pays, renews, fails a payment or cancels.

⏱ About 15 minutes

Your app needs to know when to give a customer access and when to take it away. Claspine sends a short, signed message (a callback) to your app whenever something changes, and keeps retrying for about three days if your app is down. You can also ask for the current state at any time with payments.getSubscription(subject), which is useful right after a customer returns from checkout.

Words you’ll see

Webhook
A message the provider sends to Claspine when something happens (for example a payment succeeds), so Claspine learns about it right away.
Signing secret
A secret shared between the provider and Claspine. Each webhook message is signed with it, so Claspine can prove the message really came from the provider.
Test mode and live mode
Payment providers keep two separate worlds: test mode, where nothing is really charged and you use test cards, and live mode, where real money moves. Each has its own keys.

Before you start

  • Payments set up as in “Connect your Stripe account”.
  • An https address in your app that can receive POST requests, for example https://api.example.com/spine/payments.

Steps

  1. In the Claspine console open Payments → Tell your app, paste your callback URL and click Save payment settings.
  2. Claspine shows a signing secret (starts with spcb_) once. Copy it and store it in your app’s environment, for example as SPINE_CALLBACK_SECRET.
  3. In your app, read the request body as raw text (before any JSON parsing) and check it with the SDK, as in the example below. If the check fails, answer with status 400 and ignore the message.
  4. Act on the event: for subscription.updated, give access when data.entitled is true and remove it when it is false. For order.updated, fulfil the order when data.status is CAPTURED.
  5. Answer with any 2xx status quickly (within 10 seconds). Do slow work afterwards. Any other answer makes Claspine retry later.
  6. Click Send test event on the Payments page and watch it appear under Recent events with “HTTP 200”.
Example (Node.js with Express). The same check works in Next.js route handlers and other frameworks: pass the raw body and the Spine-Signature header.
import express from 'express';
import { verifyWebhookSignature } from '@grahmur-org/claspine-client';

const app = express();
app.post('/spine/payments', express.text({ type: '*/*' }), async (req, res) => {
  let event;
  try {
    event = await verifyWebhookSignature(req.body, req.get('Spine-Signature'), process.env.SPINE_CALLBACK_SECRET);
  } catch {
    return res.sendStatus(400); // not from Claspine, or tampered with
  }
  if (event.type === 'subscription.updated') {
    await setAccess(event.data.subject, event.data.entitled); // your code
  }
  res.sendStatus(200);
});

What goes where in Claspine

Console fieldValue
Payments → CALLBACK URLYour app’s https address that receives the events.
Your app’s environmentThe spcb_… signing secret shown once when you save the URL.

Keep it safe

  • Always check the signature. Without it, anyone could send your app a fake “payment succeeded”.
  • The same event can arrive more than once (for example after a retry). Make your handler safe to run twice: setting access to the same value twice is fine.
  • Each event says whether it is test or live. Make sure test events never unlock anything in your production app.
  • If the secret leaks, click Create new secret and update your app right away.

Claspine stores the values encrypted, never shows them again in full, and never sends them to your app or SDK.

If something goes wrong

Recent events shows “Could not reach your endpoint”.
The URL must be public and use https. Addresses inside private networks are refused for safety.
Your app always rejects the signature.
Pass the raw body text, not JSON that was parsed and turned back into text; the bytes must match exactly. Also check the secret is the latest one.

← All setup guides