SaaS billing with a merchant of record: plans, trials, usage charges and webhooks
Skalp can bill your SaaS as merchant of record. Your app opens a subscription checkout, Skalp takes the payment, adds the VAT for the customer's country and issues every invoice, and signed webhooks keep your database in step. Usage above the plan goes on the same card.
Plans
Each plan is a product billed every week, month or year, with an optional trial of up to 14 days. Prices are net and set in your store currency. You can also give a plan a round price in up to three other currencies for customers in Poland, the euro area, the US and Brazil.
Opening the checkout
client := skalp.New(skalp.Options{})
session, err := client.CreateSubscriptionCheckout(ctx, skalp.CreateSubscriptionCheckoutParams{
ProductSlug: "pro-monthly",
ExternalRef: orgID,
ReturnURL: "https://app.example.com/billing?checkout=success",
})
if err != nil {
return err
}
http.Redirect(w, r, session.URL, http.StatusSeeOther)
The same call in Node is skalp.subscriptions.createCheckout(...). The customer pays on Skalp's page, so your app never renders a card form. A company in the EU can enter its VAT number there and buys under the reverse charge. ExternalRef comes back on every event for the subscription, which saves you from storing Skalp ids before the first payment.
Webhooks for the whole lifecycle
| Event | When it arrives |
subscription.created | The subscription has started. |
subscription.renewed | A period was paid. Carries the invoice id, the amount and the new end date. |
subscription.past_due | A renewal failed. Carries the attempt count and the next retry. |
subscription.updated | Something changed, for example the plan, or a cancellation scheduled for the end of the period. |
subscription.canceled | The plan has ended. Carries cancelReason. |
subscription.refunded, subscription.disputed | A payment was refunded, or the customer's bank opened a dispute. |
Every request carries an HMAC-SHA256 signature in the Skalp-Signature header. skalp.webhooks.constructEvent in Node and client.ConstructEvent in Go check it and reject deliveries older than five minutes.
Plan changes and cancellations
subscriptions.changePlan moves a customer to another plan and prorates the difference. subscriptions.cancel stops the plan at the end of the paid period, so the customer keeps access until then.
Usage charges
const charge = await skalp.subscriptions.createUsageCharge(subscription.id, {
amount: 12.34,
currency: 'EUR',
description: 'Seats over the plan, September 2026',
idempotencyKey: `seats:${orgId}:2026-09`,
periodStart: new Date('2026-09-01T00:00:00Z'),
periodEnd: new Date('2026-10-01T00:00:00Z'),
})
if (charge.status === 'open') await sendPaymentLink(charge.hostedUrl)
Usage charges are billed in your store currency to the card on file. The same idempotencyKey always returns the same charge, so you can retry after a timeout or a 5xx without billing twice. Each subscription has a daily cap on usage charges, which Skalp support can raise.
Failed payments
When a renewal fails, the customer gets an open invoice they can pay with any card, and that card is used for later renewals. You get subscription.past_due and can show a banner in your app until subscription.renewed arrives.
Invoices, coupons and tax
Skalp issues an invoice PDF for every payment with the VAT of the customer's country, and subscriptions.listInvoices returns them for your billing page. Coupons on a plan can apply to the first payment, to a set number of months or to the whole subscription.
Questions people ask
Whose name is on the invoice?
Skalp's. Skalp is the merchant of record, so it sells the plan to your customer and pays you your share.
Can I keep my own pricing page?
Yes. Your pricing page calls the API and sends the customer to the checkout it returns.
Does the customer need a Skalp account?
No. Customers who come through the API manage their subscription with a code sent to their email.
What does it cost?
10% of each payment, or 7% once your sales over the last 30 days pass $20,000, with payment processing included and no monthly fee.