How to Build a Custom Payments Integration on Sparkforge

By Sparkforge Support Team ·Last reviewed May 3, 2026

Build a custom payment provider by creating a Marketplace app, implementing OAuth and a queryUrl backend, hosting a checkout iframe at paymentsUrl, and sending webhooks back to Sparkforge. The verify response — not webhooks — sets the initial subscription state.

Sparkforge’s custom payment provider framework lets you plug any gateway into the platform and use it everywhere Sparkforge processes payments — checkouts, subscriptions, saved cards, refunds, off-session charges. This guide walks the full build.

What you can do once integrated

  • Show up in the App Marketplace and Payments → Integrations.
  • Power one-time and recurring payments.
  • Run off-session charges with stored payment methods.
  • Support manual subscriptions and subscription schedules.
  • Sync state to Sparkforge via webhooks.

Key concepts

  • Marketplace App — the container for your integration: OAuth, scopes, category, custom pages.
  • Custom Payment Provider — the payments config telling Sparkforge your app is a payment provider and which payment types you support.
  • queryUrl — your backend endpoint Sparkforge calls for server-side ops: verify, list_payment_methods, charge_payment, create_subscription, cancel_subscription, refund.
  • paymentsUrl — the public URL Sparkforge loads in an iframe to collect payments.
  • Custom Page — the public URL Sparkforge loads in an iframe in the Marketplace UI for config (API keys, merchant IDs, modes).
  • Test vs Live mode — each location holds separate config for both.

Prerequisites

  • A Sparkforge account with Marketplace Dashboard access.
  • A backend service in any language (Node, Python, Ruby, etc.).
  • Public domains for OAuth redirect, custom page, queryUrl, webhooks, and paymentsUrl.
  • Working knowledge of OAuth 2.0, JSON HTTP APIs, and webhooks.

Step 1 — Create your Marketplace App

Go to marketplace.sparkforge.com and create an app. Configure four sections: Settings, Payment Provider, Profile, Custom Pages.

Required scopes

payments/orders.readonly
payments/orders.write
payments/subscriptions.readonly
payments/transactions.readonly
payments/custom-provider.readonly
payments/custom-provider.write
products.readonly
products/prices.readonly

Redirect URL

Sparkforge redirects users here with a code query parameter on install. Exchange the code for an access token using the OAuth Access Token API and store the token securely.

Client keys

Sparkforge issues OAuth client keys. Keep them server-side only and use them when exchanging the code for a token.

Webhook URL (install/uninstall)

Sparkforge calls this when your app installs at or uninstalls from a location. Use it to bootstrap or tear down per-location config.

SSO key

Stored on your backend, used to decrypt the auth token Sparkforge passes to your custom page so you can identify the user and location.

Payment Provider config

  • Name — display name in Sparkforge UI.
  • App description — short text on Payments → Integrations.
  • Provider type — OneTime, Recurring, Off Session (one or more).
  • Logo — public URL for the icon.

Profile

Set Category = Third Party Provider. This places your app correctly in the App Marketplace and Payments → Integrations.

Custom Page

The iframe-loaded config page for collecting gateway credentials. Used post-install and again when a user clicks Manage Integration under Payments → Integrations.

Step 2 — Install flow and provider config

On install:

  1. Sparkforge calls your redirect URL with a code.
  2. Your backend exchanges the code for an access token.
  3. Sparkforge loads your custom page.
  4. Your backend calls the Create Public Provider Config API to register the provider.
{
  "name": "My Payment Provider",
  "description": "Tagline",
  "imageUrl": "https://your-domain.com/logo.png",
  "locationId": "SUB_ACCOUNT_ID",
  "queryUrl": "https://your-backend.com/payments/query",
  "paymentsUrl": "https://your-frontend.com/payments/checkout"
}

Step 3 — Connect test and live keys

For each mode, call the Connect Config API with:

  • apiKey — backend secret used to authenticate Sparkforge → your queryUrl.
  • publishableKey — frontend-safe key for the iframe.

Once test or live keys are present, that mode becomes usable. Users set the provider as default at Payments → Integrations → [Your App] → Set as Default.

Step 4 — Implement the checkout iframe

Ready event

{ "type": "custom_provider_ready", "loaded": true }

Payment initiate

Sparkforge sends a payment_initiate_props event with publishable key, amount, currency, mode (payment or subscription), productDetails, contact (id, name, email, phone, shipping address), and IDs for order, transaction, subscription, and location. Use it to start the gateway flow.

Outcome events

  • Success: { "type": "custom_element_success_response", "chargeId": "GATEWAY_CHARGE_ID" }
  • Failure: { "type": "custom_element_error_response", "error": { "description": "Card was declined" } }
  • Canceled: { "type": "custom_element_close_response" }

Verify call

After a success event, Sparkforge POSTs type: "verify" to your queryUrl with transactionId, chargeId, optional subscriptionId, and your apiKey. Respond with one of:

  • { "success": true } — marks the order/transaction successful.
  • { "failed": true } — marks failed.
  • { "success": false } — leaves it pending; you can update later via webhooks.

Step 5 — Saved payment methods and manual subscriptions

Add card on file

Set addCardOnFileSupported: true on the ready event. Sparkforge then sends setup_initiate_props for setup-mode flows. Reply with success or error events the same way.

List payment methods

Sparkforge calls queryUrl with type: "list_payment_methods" and a contactId. Return an array of { id, type, title, subTitle, expiry, customerId, imageUrl }. You’re responsible for keeping the contact ↔ customer mapping on your side.

Charge a saved method

For off-session charges, Sparkforge sends type: "charge_payment" with paymentMethodId, contactId, transactionId, chargeDescription, amount, currency, and apiKey. Return success, chargeId, and a chargeSnapshot containing status, amount, chargeId, and Unix chargedAt.

Manual subscriptions and schedules

Declare support via the Update Capabilities API (companyId or locationId, OAuth scope required). Sparkforge then allows admin-created subscription schedules.

At runtime, Sparkforge POSTs type: "create_subscription" with locationId, contactId, paymentMethodId, subscriptionId, transactionId, startDate, currency, amount, recurringAmount, and full productDetails. Return a required subscriptionSnapshot (status: scheduled, trialing, active, expired, canceled, unpaid, incomplete) and an optional transaction for any initial charge.

Cancel subscription

For type: "cancel_subscription", return { "status": "canceled" }.

Step 6 — Refunds

Sparkforge sends type: "refund" with amount, transactionId, chargeId, and apiKey. Refunds can be partial; multiple refunds are allowed up to the original charge total. Respond with success, id, amount, currency, and a message.

Step 7 — Webhooks back to Sparkforge

POST JSON to https://backend.sparkforgehq.com/payments/custom-provider/webhook.

Supported events

  • subscription.trialing
  • subscription.active
  • subscription.updated
  • subscription.charged
  • payment.captured

Each payload includes event, chargeId, ghlSubscriptionId, ghlTransactionId, marketplaceAppId, locationId, apiKey, plus subscriptionSnapshot and chargeSnapshot where relevant.

Go-live checklist

  • Marketplace app created with correct scopes and all four config sections filled.
  • OAuth redirect collects code; backend exchanges for token; install/uninstall webhooks handled.
  • Create Public Provider Config called on install with correct queryUrl and paymentsUrl.
  • Test and live config saved via Connect Config; provider set as default.
  • Iframe emits custom_provider_ready, handles payment_initiate_props, emits success/error/close.
  • Backend handles verify with success / failed / pending semantics.
  • Saved methods supported via addCardOnFileSupported, setup_initiate_props, list_payment_methods, charge_payment.
  • Subscription schedules enabled via Update Capabilities; create_subscription and cancel_subscription handled.
  • Refunds handled.
  • Webhooks emitted for all relevant events.
  • End-to-end flows passed in test mode and re-run in live before launch.

Frequently Asked Questions

What languages can I use to build the integration?
Anything that speaks HTTP and JSON — Node.js, Python, Ruby, PHP, Java, Go. The Sparkforge API is language-agnostic.
My subscription stays Incomplete even after I send a subscription.active webhook. Why?
The verify response, not the webhook, sets the initial subscription state. Return success: true plus a chargeSnapshot and subscriptionSnapshot (with status: active) from your verify handler. Webhooks are for state changes after that.
When do I send subscription.trialing vs subscription.active vs subscription.charged?
Trialing right after creating a trial with no initial charge. Active after the first successful charge that moves the sub from trial/pending into ongoing billing. Charged after every recurring charge.
Who runs the recurring billing cycles?
You do. Sparkforge does not run recurring charges for custom providers. Store the schedule, charge on each billing date through your gateway, and send subscription.charged webhooks so Sparkforge stays in sync.
Why don’t custom-provider saved cards show up under Contact → Payments → Manage Cards?
Current behavior: cards saved via custom providers are available when picking a saved card on a subscription, but not in the Manage Cards UI. Full Manage Cards visibility is planned but not shipped yet.
Can one integration handle multiple gateways?
Yes. Maintain per-gateway config on your backend and route requests by location, currency, or business rules. Sparkforge just sees one provider talking to your queryUrl.

Was this article helpful?