Skip to Content
PekoPay API Integration Docs
DocumentationDeveloper GuideAPI Examples and Workflows

API Examples and Workflows

Assume these variables:

  • BASE_URL
  • TENANT_ID
  • AUTH

Real-life integration patterns

Example 1: E-commerce one-time checkout (guest or existing customer)

Use this when a shopper is buying products in a cart and you want to collect payment immediately.

How to implement:

  1. In your frontend, open PekoPay Checkout and get a paymentToken from the success callback.
  2. In your backend, ensure the shopper exists as a customer (create or reuse by email).
  3. Create the transaction with cart items and tax options.
  4. Save the returned transaction ID in your order system and show a success page.

Step-by-step: get and use a payment token

Use this exact sequence in an e-commerce checkout:

  1. Your backend requests a checkout token from PekoPay.
  2. Your frontend mounts the embedded checkout form with that token.
  3. Customer submits card details through the embedded form.
  4. In completeTransaction, use data.sessionToken as the payment token.
  5. Your frontend sends that paymentToken to your backend.
  6. Your backend creates the one-time transaction.

Backend step 1: generate checkout token

const tokenResponse = await fetch(`${baseUrl}/payment-token/${tenantId}/generate`, {
  method: 'POST',
  headers: {
    Authorization: `Basic ${auth}`,
    'Content-Type': 'application/json'
  }
});
 
if (!tokenResponse.ok) throw new Error('Failed to generate checkout token');
const { token } = await tokenResponse.json();
// Return token to frontend

Frontend step 2 to 5: embed checkout form and capture paymentToken

import { PekoCheckoutForm } from '@pekopay/web';
 
const checkoutFormContainer = document.getElementById('credit-card-form-container');
const payButton = document.getElementById('pay-button');
 
const form = PekoCheckoutForm.initialize({
  sessionToken: token, // from your backend /payment-token/:tenantId/generate
  environment: 'sandbox',
  container: checkoutFormContainer,
  height: 160,
  width: 300,
  onFormFieldStateChange: (state) => {
    console.log('Form field state changed:', state);
  },
  onLoadError: (error) => {
    console.error('Form load error:', error);
  },
  completeTransaction: async (data) => {
    // IMPORTANT: use data.sessionToken (can be refreshed token)
    const paymentToken = data.sessionToken;
 
    await fetch('/api/checkout/complete', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ paymentToken, cartId })
    });
 
    // Optional: selected card details for future card selection flows
    const selectedCard = {
      cardLastFourDigits: data.cardData.last4Digits,
      cardType: data.cardData.cardType
    };
 
    console.log('Selected card:', selectedCard);
  },
  sessionTokenProvider: async () => {
    // Called if the current token expires; fetch and return a fresh token
    const response = await fetch('/api/checkout/token');
    const { token: refreshedToken } = await response.json();
    return refreshedToken;
  }
});
 
form.mount();
 
payButton?.addEventListener('click', () => {
  form.submitFormFields();
});

Backend step 4 and 5: use paymentToken to create transaction

curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/transactions" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentToken": "sess_from_checkout_callback",
    "customerId": "'$CUSTOMER_ID'",
    "items": [
      { "productId": "'$PRODUCT_ID'", "quantity": 2 }
    ],
    "taxRatesOptions": ["gst", "pst"]
  }'

Important:

  • Never create transactions directly from the frontend.
  • Always validate cart totals on the backend before charging.
  • Treat paymentToken as short-lived and one-time use.
  • Always use the latest data.sessionToken from completeTransaction (not the initial token).

Typical backend flow:

# 1) Create customer (skip if already stored in your DB)
curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/customers" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "firstname": "Sarah",
    "lastname": "Ng",
    "email": "sarah@example.com",
    "type": "INDIVIDUAL"
  }'
 
# 2) Charge a one-time transaction using paymentToken from checkout callback
curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/transactions" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentToken": "sess_from_checkout_callback",
    "customerId": "'$CUSTOMER_ID'",
    "items": [
      { "productId": "'$PRODUCT_ID'", "quantity": 2 }
    ],
    "taxRatesOptions": ["gst", "pst"]
  }'

Production tips:

  • Treat your backend as source of truth: never trust price/quantity from frontend only.
  • Make your order creation idempotent using your own request key to avoid double charges.
  • Persist both your internal order ID and PekoPay transaction ID for reconciliation.

Example 2: SaaS subscription signup with trial and plan upgrades

Use this when a customer starts on a trial, becomes active billing, and may upgrade later.

How to implement:

  1. Create a monthly plan (once, by admin tooling or provisioning script).
  2. At signup, collect paymentToken through checkout.
  3. Create subscription for the selected plan and taxes.
  4. If user upgrades, call the switch-plan endpoint.
  5. Handle subscription lifecycle updates (success/failure) through your webhook processing.

Typical backend flow:

# 1) Create a plan (one-time setup)
curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/plans" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Growth Monthly",
    "description": "Growth tier for teams",
    "recurringChargeAmount": 99.00,
    "chargeFrequency": "MONTHLY",
    "currency": "CAD",
    "trialPeriodDays": 14,
    "gracePeriodDays": 7,
    "active": true
  }'
 
# 2) Create subscription at signup
curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/subscriptions" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentToken": "sess_from_checkout_callback",
    "subscription": {
      "planId": "'$PLAN_ID'",
      "customerId": "'$CUSTOMER_ID'",
      "quantity": 1,
      "applicableTaxes": ["gst", "pst"]
    }
  }'
 
# 3) Upgrade later
curl -X PATCH "${BASE_URL}/tenants/${TENANT_ID}/subscriptions/${SUBSCRIPTION_ID}/switch" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{ "planId": "'$NEW_PLAN_ID'" }'

Production tips:

  • Store plan IDs in your config layer instead of hardcoding in frontend code.
  • Model entitlements from your own DB state, updated by subscription lifecycle events.
  • Add retry + dead-letter handling for webhook ingestion so billing state stays consistent.

Customers

Create:

curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/customers" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "firstname": "Jane",
    "lastname": "Doe",
    "email": "jane@acme.com",
    "phone": "+1-604-555-1111",
    "type": "INDIVIDUAL"
  }'

Update:

curl -X PUT "${BASE_URL}/tenants/${TENANT_ID}/customers/${CUSTOMER_ID}" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "firstname": "Janet",
    "lastname": "Doe",
    "email": "janet@acme.com"
  }'

Delete:

curl -X DELETE "${BASE_URL}/tenants/${TENANT_ID}/customers/${CUSTOMER_ID}" \
  -H "Authorization: Basic ${AUTH}"

Products

Create:

curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/products" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Premium Support",
    "description": "Monthly support plan",
    "price": 49.99,
    "currency": "CAD",
    "active": true
  }'

One-time transaction

Endpoint:

  • POST /tenants/:tenantId/transactions
curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/transactions" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentToken": "sess_from_checkout_callback",
    "customerId": "'$CUSTOMER_ID'",
    "items": [
      { "productId": "'$PRODUCT_ID'", "quantity": 2 }
    ],
    "taxRatesOptions": ["gst", "pst"]
  }'

Subscriptions

Create plan:

curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/plans" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Starter Monthly",
    "description": "Starter package",
    "recurringChargeAmount": 29.99,
    "chargeFrequency": "MONTHLY",
    "currency": "CAD",
    "trialPeriodDays": 14,
    "gracePeriodDays": 7,
    "active": true
  }'

Create subscription:

curl -X POST "${BASE_URL}/tenants/${TENANT_ID}/subscriptions" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentToken": "sess_from_checkout_callback",
    "subscription": {
      "planId": "'$PLAN_ID'",
      "customerId": "'$CUSTOMER_ID'",
      "quantity": 1,
      "applicableTaxes": ["gst", "pst"]
    }
  }'

Switch plan:

curl -X PATCH "${BASE_URL}/tenants/${TENANT_ID}/subscriptions/${SUBSCRIPTION_ID}/switch" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{ "planId": "'$NEW_PLAN_ID'" }'

Cancel:

curl -X DELETE "${BASE_URL}/tenants/${TENANT_ID}/subscriptions/${SUBSCRIPTION_ID}" \
  -H "Authorization: Basic ${AUTH}"

Lifecycle operations

Add payment method to existing customer:

curl -X PUT "${BASE_URL}/tenants/${TENANT_ID}/customers/${CUSTOMER_ID}/payment-methods" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{ "paymentToken": "sess_from_checkout_callback" }'

Choose default card:

curl -X PUT "${BASE_URL}/tenants/${TENANT_ID}/customers/${CUSTOMER_ID}/credit-card/choose" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{
    "cardLastFourDigits": "1111",
    "cardType": "VISA"
  }'

Update subscription quantity:

curl -X PATCH "${BASE_URL}/tenants/${TENANT_ID}/subscriptions/${SUBSCRIPTION_ID}/quantity" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{ "quantity": 5 }'

Update subscription payment source:

curl -X PATCH "${BASE_URL}/tenants/${TENANT_ID}/subscriptions/${SUBSCRIPTION_ID}/payment-source" \
  -H "Authorization: Basic ${AUTH}" \
  -H "Content-Type: application/json" \
  -d '{ "pfToken": "sess_new_token_from_checkout" }'

Node.js API example (no SDK)

const baseUrl = process.env.PEKOPAY_BASE_URL!;
const tenantId = process.env.TENANT_ID!;
const clientId = process.env.CLIENT_ID!;
const clientSecret = process.env.CLIENT_SECRET!;
 
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
 
const request = async (path: string, init?: RequestInit) => {
  const response = await fetch(`${baseUrl}${path}`, {
    ...init,
    headers: {
      Authorization: `Basic ${auth}`,
      'Content-Type': 'application/json',
      ...(init?.headers ?? {})
    }
  });
 
  if (!response.ok) {
    throw new Error(`PekoPay request failed: ${response.status}`);
  }
 
  return response.json();
};
 
const token = await request(`/payment-token/${tenantId}/generate`);
const products = await request(`/tenants/${tenantId}/products`);
const customers = await request(`/tenants/${tenantId}/customers`);
Last updated on