Skip to content

Payment providers

SAPA’s public facade is provider-neutral. A runtime installs a provider registry whose adapters create checkouts, authenticate notifications, normalize status, and reconcile retained attempts.

A provider adapter must:

  1. authenticate the exact raw notification payload;
  2. bind the notification to the expected provider and checkout;
  3. compare currency and exact amount before settlement;
  4. retain stable provider event identity for deduplication;
  5. classify pending, settled, failed, expired, and refunded observations;
  6. sanitize provider failures before they cross the facade;
  7. treat checkout-creation timeout as an unknown outcome;
  8. reconcile existing attempts without initiating duplicate creation.

Your HTTP route passes provider identity, the untouched raw body, and only the headers needed by the provider’s signature scheme:

import { processPaymentNotification } from "@byfungsi/sapa";
const operation = processPaymentNotification({
provider: "polar",
payload: rawBody,
headers: {
"webhook-id": request.headers.get("webhook-id") ?? "",
"webhook-timestamp": request.headers.get("webhook-timestamp") ?? "",
"webhook-signature": request.headers.get("webhook-signature") ?? "",
},
});

Do not parse and reserialize a signed payload unless the provider’s signature scheme explicitly requires that representation.

The Cloudflare reference runtime already exposes provider-specific routes. Polar currently supports USD one-time checkout, authenticated order.paid, complete order.refunded, and checkout.expired events. Partial refunds are authenticated but ignored until Polar reports the cumulative full charged amount; SAPA never revokes the complete purchase for a partial refund. Automatic recurring Polar billing is not advertised until renewal orders can be mapped to SAPA subscription cycles without reusing the initial checkout settlement.

Polar settlement compares SAPA’s price with net_amount, the amount after discounts and before sales tax. A tax-inclusive total_amount may therefore be higher without weakening the exact price check. A complete refund still requires the cumulative refunded_amount to equal that tax-inclusive total.

Network retries are not universally safe. Retrying an idempotent status read can be safe; blindly retrying checkout creation can duplicate an external charge or payment session. Preserve SAPA’s checkout ID and idempotency key, then use recoverCheckout for reconciliation.

@byfungsi/sapa exposes the provider-neutral facade. Concrete provider and storage adapters are composed by the host deployment and are not separate public npm entrypoints in 0.1.0.