Developer documentation
Create payment links from your backend, receive secure callbacks, and deliver WhatsApp confirmations through 1Confirmed.
Quick start
- Create a merchant in the console and configure Payzone with your paywall URL, merchant account and secret keys.
- Generate an API key (prefix
pg_live_). The plaintext value is shown once. - Call the REST API to create a payment link.
- Forward the returned
payment_urlto your customer (WhatsApp, email, SMS…). - Receive a server-to-server webhook on terminal status.
Authentication
Every REST request must include a bearer API key. Treat keys as secrets and rotate them via the console.
Authorization: Bearer pg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxCreate payment link
POST /api/public/v1/payment-links
curl -X POST https://pg.1confirmed.com/api/public/v1/payment-links \
-H "Authorization: Bearer pg_live_..." \
-H "Content-Type: application/json" \
-d '{
"gateway": "payzone",
"amount": 125.00,
"currency": "MAD",
"description": "Invoice #A-1042",
"reference": "INV-A-1042",
"customer": {
"name": "Sara Bennani",
"email": "sara@example.com",
"phone": "+212600112233"
}
}'| Field | Type | Notes |
|---|---|---|
gateway | string | Defaults to payzone. |
amount | number | Decimal in major unit. 50.00 = 50 MAD. Not cents. |
currency | string (ISO 4217) | 3 letters, e.g. MAD. Defaults to MAD. |
description | string | Optional, max 500 chars. |
reference | string | Optional. Auto-generated (PG-…) if omitted. |
customer.name | string | Optional. |
customer.email | string | Optional, must be a valid email. |
customer.phone | string | Optional, max 40 chars. |
Common mistakes: do not send amount_cents — use amount in the major unit. Do not flatten customer fields as customer_name / customer_email / customer_phone — they must be nested under customer. Unknown keys are silently dropped by the validator, so a malformed payload may create a link with missing data instead of returning an error.
Response:
{
"id": "8b9b…",
"reference": "INV-A-1042",
"payment_url": "https://pg.1confirmed.com/pay/8b9b…",
"status": "pending",
"amount": 125.00,
"currency": "MAD",
"created_at": "2026-06-23T13:45:01.000Z"
}Gateway callbacks (Payzone)
Configure your Payzone account callback URL to https://pg.1confirmed.com/api/public/callback/{merchantCode}. PG verifies the X-Callback-Signature HMAC-SHA256 header against the merchant secret before updating the payment status — invalid signatures return 401.
Stripe setup
Stripe uses hosted Checkout. Each merchant connects with their own keys; credentials are encrypted at rest (AES-256-GCM) and only ever leave the database to sign API calls.
- In the Stripe Dashboard, copy your Secret key (
sk_test_…while testing,sk_live_…in production). The publishable key is optional. - In the PG console, open Gateways → Stripe → Configure, paste the secret key, and pick a webhook setup mode.
- Auto-provision (recommended): we call Stripe on your behalf to create a webhook endpoint scoped to your merchant and store the signing secret. Nothing else for you to do.
- Manual: copy the endpoint URL we show you (
https://pg.1confirmed.com/api/public/stripe-webhook/{merchantCode}), add it in Stripe Dashboard → Developers → Webhooks, subscribe to the events listed below, then paste thewhsec_…signing secret back into the dialog.
Events handled in V1:
checkout.session.completed→payment.succeededcheckout.session.async_payment_succeeded→payment.succeededcheckout.session.async_payment_failed→payment.failedpayment_intent.payment_failed→payment.failedcheckout.session.expired→payment.cancelled
When creating a payment link via the REST API, set "gateway": "stripe". Refunds, disputes, subscriptions, and Stripe Connect onboarding are not in V1.
Outbound webhooks
Register one or more endpoints from the console. Every delivery is signed:
POST https://your-app.example.com/hooks/pg
x-pg-event: payment.succeeded
x-pg-signature: hex(hmac_sha256(endpoint_secret, body))
{
"event": "payment.succeeded",
"created_at": "2026-06-23T13:45:01.000Z",
"data": {
"payment_request_id": "8b9b…",
"reference": "INV-A-1042",
"gateway": "payzone",
"gateway_transaction_id": "PZN-9817263",
"status": "succeeded"
}
}Verify the signature with a constant-time comparison. Failed deliveries are retried automatically with an exponential schedule (5m, 15m, 1h, 4h, 12h) before being marked exhausted.
Event types
payment.processing— gateway acknowledged but not finalized.payment.succeeded— funds captured.payment.failed— gateway returned a terminal failure.payment.cancelled— customer cancelled before completion.
WhatsApp delivery (1Confirmed)
When a payment reaches a terminal state and a customer_phone was provided, PG dispatches a WhatsApp confirmation through the 1Confirmed messaging API. This requires the ONECONFIRMED_API_KEY backend secret. When the secret is absent the rest of the flow continues unchanged.
Errors
The API uses standard HTTP status codes and returns a JSON body:
{ "error": "Invalid signature" }400— validation error (checkerrorfield).401— missing/invalid API key or callback signature.403— API key does not belong to the requested merchant.404— unknown merchant or payment reference.503— gateway not configured for this merchant.