API Reference

Introduction

The Subscriptions API allows merchants to manage subscription lifecycles — creating customers, memberships, and sessions — through a secure REST interface. Built for seamless integration with Paytiko subscription services.


Prerequisites

Before integrating with the Paytiko Subscriptions API, ensure that the following requirements are met:

  • You must have valid API credentials (issued by Paytiko).

  • All communication must be over HTTPS only.

  • You must know your processorKey, and be able to retrieve and persist the customerKey and membershipKey via the API.

  • Your backend must support JWT-based authentication for including the Authorization: Bearer <token> header.

  • You should implement secure storage of credentials, tokens, and keys (never expose secrets to the client/browser).

  • You must configure and expose a publicly accessible webhook endpoint to receive subscription and payment event notifications from Paytiko.

  • You should handle idempotency when processing webhook events (e.g., avoid duplicate handling).

  • Ensure your system’s time zone and clocks are synchronized (UTC recommended), since subscription cycles and endDate validation are time-sensitive.

  • Your customer and product data should meet validation requirements (ISO country codes, valid email/phone formats, well-formed URIs).

  • You must implement error handling for API responses (HTTP 4xx, 5xx) to gracefully manage failed requests.

  • For checkout and portal sessions, ensure you can redirect customers to the hosted Paytiko pages and handle return URLs.


Best Practices

To build a robust and reliable integration, consider the following recommendations:

  • Token Management:

    • Refresh tokens before they expire (tokens are short-lived).

    • Do not hardcode tokens; request them dynamically when needed.

  • Webhook Reliability:

    • Treat all webhook requests as asynchronous events.

    • Always return HTTP 200 OK after successful processing.

    • Implement a retry strategy: if your system fails temporarily, Paytiko may resend webhooks — handle duplicates safely.

  • Security:

    • Verify incoming webhook requests (e.g., by checking signatures or IP allowlists if provided by Paytiko).

    • Use HTTPS everywhere; never log sensitive data such as passwords or full card numbers.

  • Data Integrity:

    • Validate incoming fields against your business logic (currency, amounts, interval counts).

    • Store customerKey and membershipKey in your database to link Paytiko records with your own user accounts.

  • Error Handling:

    • Retry failed API calls using exponential backoff.

    • Log both request and response data (excluding sensitive information) for debugging and reconciliation.


Technical Flow

This section describes the end-to-end lifecycle of how a subscription is created, purchased, and billed using the Paytiko Subscriptions API. The process covers authentication, customer creation, checkout, payment confirmation, invoicing, and recurring billing.

  1. Customer requests a subscription The flow begins when a customer chooses a subscription plan on the merchant’s website or application.

  2. Merchant authenticates The merchant uses its Paytiko-issued API credentials to obtain a secure access token. This token is required for all subsequent API requests.

  3. Merchant creates a customer Using the customer’s details (name, email, phone, billing info), the merchant calls Paytiko to register a new customer. Paytiko returns a unique customerKey, which the merchant stores in its own database.

  4. Merchant creates a membership The merchant associates the customer with a subscription plan by creating a membership. This defines pricing, billing intervals, and product details. Paytiko returns a membershipKey.

  5. Merchant initiates a checkout session To complete the purchase, the merchant creates a checkout session linked to the customerKey and membershipKey. Paytiko responds with a redirectUrl, and the merchant redirects the customer to the hosted checkout page.

  6. Customer confirms identity and pays The customer interacts with Paytiko’s secure checkout to confirm their identity and complete payment.

  7. Paytiko notifies the merchant Upon successful payment (or failure), Paytiko sends a webhook notification to the merchant. At the same time, an invoice or receipt is emailed directly to the customer.

  8. Recurring billing cycles run automatically On each billing cycle, Paytiko processes the recurring charge. The merchant receives webhook notifications with charge results, while the customer receives invoices by email.


Endpoints

This section provides detailed documentation for all available Paytiko Subscriptions API endpoints. Each endpoint description includes:

  • Method & Path – The HTTP method (e.g., POST, GET) and full URI.

  • Overview – A short description of the purpose of the endpoint.

  • Headers – Required and optional HTTP headers.

  • Request Body – JSON model with all fields and validation rules.

  • Response – Example success and error responses.

All requests must be authenticated using a JWT bearer token obtained via the Authentication endpoint.


Try It Yourself — Postman Collection

To get started quickly, you can import our Postman Collection, which contains ready-to-use requests for all endpoints:

  • Preconfigured with request URLs and headers

  • Includes example payloads for authentication, customer creation, membership creation, and checkout

  • Helps you test the flow end-to-end before integrating into your system


Authentication

POST /api/subscriptions/v1/auth

The authentication endpoint is used to obtain a JWT bearer token. This token is required in the Authorization header for all other API requests.

Request Headers

Name
Value
Required

Content-Type

application/json

Request Body

Name
Type
Description
Required

userName

string

User name

password

string

Password

Response

{
    "token": "<token>",
    "expiresIn": 720 // seconds.
}

Customers

Create Customer

POST /api/subscriptions/v1/customer

This endpoint registers a new customer in the subscription system. The customer is the entity that owns memberships, billing details, and sessions.

Use case: When a new end user signs up, you must create their customer profile before adding memberships.

Customer uniqueness A customer is uniquely identified by their email address within the scope of a single merchant.

This request is idempotent

Request Headers

Name
Value
Required

Content-Type

application/json

Authorization

Bearer <token>

Request Body

Name
Type
Required
Description

processorKey

string (UUID)

Processor identifier.

Must be a non-empty GUID (canonical UUID).

firstName

string

Customer first name.

Must match ^\p{L}+(?:[ -]\p{L}+)*$ → letters only (Unicode), hyphens and spaces.

lastName

string

Customer last name.

Must match ^\p{L}+(?:[ -]\p{L}+)*$ → letters only (Unicode), hyphens and spaces.

email

string

Customer email.

Must pass server email validation, standard name@domain.tld.

phone

string

Customer phone number.

Must match the E.164 international format (regex pattern is ^+[1-9]\d{7,14}$).

  • Must begin with a “+” sign followed by the country code and subscriber number

  • Cannot start with 0 after the +

  • Must be between 8 and 15 digits total (excluding the +)

  • No spaces, hyphens, or special characters allowed.

countryCode

string

Country (ISO-3166-1 alpha-2).

Exactly 2 letters and must be recognized by the server’s country list; case-insensitive (use uppercase like US, PL).

address1

string

Street address line 1.

Optional; no additional validation in current model.

address2

string

Apartment/Suite.

Optional; no additional validation.

city

string

City/Locality.

Optional; no additional validation.

state

string

State/Region/Province.

Optional; no additional validation.

zipCode

string

Postal/ZIP code.

Optional; no additional validation.

taxIds

array

Business tax identifiers. Present in cURL examples but not in the provided C# model/validator.

Each item is typically { "type": string, "value": string }. No explicit server-side validation rules provided in the shared code.

Request Body Sample
{
  "processorKey": "e05b87aa-b22e-4c57-9e99-b39fc219dd4f",
  "firstName": "Alice",
  "lastName": "Smith",
  "email": "alice.smith@example.com",
  "phone": "+447700900123",
  "countryCode": "GB",
  "address1": "221B Baker Street",
  "address2": "Flat 2",
  "city": "London",
  "state": "London",
  "zipCode": "NW16XE",
  "taxIds": [
    {
      "type": "VAT",
      "value": "GB123456789"
    }
  ]
}

Response

{
    "result": {
        "customerKey": "cstmr_cc0067638c2240999c53824a45db0360"
    }
}

Get Customer by Key

GET /api/subscriptions/v1/customer/{customerKey}

Retrieves full details of a previously created customer.

Use case: Fetch customer information to display in your UI or confirm that a customer exists before assigning memberships.

Headers

Name
Value
Required

Authorization

Bearer <token>

Response

{
    "result": {
        "key": "cstmr_737120202a0241888b307766308cb330",
        "firstName": "Alice",
        "lastName": "Smith",
        "countryCode": "GB",
        "email": "alice.smith@example.com",
        "phone": "+447700900123",
        "address1": "221B Baker Street",
        "address2": "Flat 2",
        "city": "London",
        "state": "London",
        "zipCode": "NW16XE",
        "taxIds": [],
        "createdDate": "2025-08-21T09:35:18.818714+00:00",
        "updatedDate": null
    }
}

Memberships

Create Membership

POST /api/subscriptions/v1/membership

This endpoint registers a new membership for a customer. A membership defines the subscription relationship, including product details, pricing, and billing cycle.

Use case: When a customer purchases a subscription plan, the merchant must create a membership linked to that customer, ensuring the subscription can be billed and managed.

Membership uniqueness A membership is uniquely identified by its orderId (external order identifier) within the scope of a single customer.

This request is idempotent

Request Headers

Name
Value
Required

Content-Type

application/json

Authorization

Bearer <token>

Request Body

Name
Type
Required
Description & Validation Rules

customerKey

string

Customer identifier.

Must start with prefix cstmr_. Used to link the membership to an existing customer.

subscriptionName

string

Human-readable plan name.

Must be non-empty. Shown in invoices/portal and useful for your internal reporting.

endDate

string

Optional subscription end date.

  • Must be in UTC ISO-8601 format (e.g. YYYY-MM-DDTHH:mm:ss).

  • Must be at least 1 hour ahead of the current UTC time.

  • If provided, no new billing cycles will start after this moment.

  • If not provided, the subscription will be infinite and continue to run until it is explicitly cancelled.

Valid examples:

  • 2025-09-01T12:00:00 → valid, 1 September 2025 at 12:00 UTC

  • 2025-08-21T19:45:00 → valid if current UTC < 18:45 UTC (at least +1h ahead)

productInfo

object

Product details.

See productInfo.

pricingInfo

object

Pricing details.

See pricingInfo.

billingCycle

object

Recurrence definition.

See billingCycle.

webhookUrl

string

Callback for lifecycle events.

Must be a well-formed absolute URL (HTTPS only).

Paytiko posts events like payment success/failure, subscription status changes, etc.

Your endpoint must return 2xx on success and be idempotent.

chargeEmitterQueue

string

Optional test/seed events.

If provided, cannot be an empty array. Each item must have transactionStatus Success or a negative enum value (per server).

Intended for test mode and QA scenarios.

See chargeEmitterQueue.

productInfo

Name
Type
Required
Description & Validation Rules

orderId

string

External order ID from your system.

Required to be unique to prevent accidental duplication.

description

string

Product description.

Optional, free-text product/order description (shown in UIs/invoices if supported).

pricingInfo

Name
Type
Required
Description & Validation Rules

currency

string

Currency code. Must be a 3-letter ISO 4217 code (e.g., USD, EUR).

totalAmount

string

Total subscription amount. Must be greater than 0.

If pricePerUnit and quantity are provided, then: totalAmount = pricePerUnit × quantity.

pricePerUnit

number

Price per unit.

Must be greater than 0 if provided. If provided together with quantity, their multiplication must equal totalAmount.

quantity

integer

Quantity of units.

Must be greater than 0 if provided. If provided together with pricePerUnit, their multiplication must equal totalAmount.

billingCycle

Name
Type
Required
Description & Validation Rules

intervalType

string

Billing interval.

Defines the base recurrence unit.

Must be one of: Daily, Weekly, Monthly, Annually.

intervalCount

integer

Interval multiplier.

Optional. Defines how many intervalType units between charges. Must be an integer ≥1 if provided. If not provided, defaults to 1.

Examples: intervalType=Weekly, intervalCount=2 → every 2

intervalType=Monthly, intervalCount=3 → every 3 months (quarterly)

Monthly Billing Date Edge-Cases are Safely-Handled

chargeEmitterQueue[]

Name
Type
Required
Description & Validation Rules

transactionStatus

string

Transaction status filter.

Must be either:

  • Success → charge is processed successfully.

  • Rejected → charge is simulated as failed.

description

string

Error message returned when transactionStatus is Rejected.

Defaults to "Insufficient funds." if not provided.

When testing subscriptions in sandbox/test mode, the payment gateway normally supports only success-resulting test cards. That means all recurring charges would succeed by default.

To simulate real-world billing conditions (e.g., declines, retries, eventual success), you can provide a chargeEmitterQueue — an array that defines the outcome of each scheduled charge.

How it works

  • Each item in the array represents the result of the next charge attempt in sequence.

  • Allowed values are:

    • success → charge is processed successfully.

    • rejected → charge fails with a simulated decline.

  • Once all items in the array are consumed, the emitter stops and all subsequent charges default to success (unless you provide a longer array).

  • This applies only to recurring charges triggered by billing cycles.

  • The initial checkout payment is not affected by chargeEmitter (it always follows test card rules).

Expanation

  • 1st chargesuccess → The first billing cycle succeeds.

  • 2nd chargerejected → The next billing attempt is rejected.

  • 3rd chargerejected → Another failure is simulated.

  • 4th chargerejected → Third consecutive failure.

  • 5th chargesuccess → Billing finally succeeds again.

Rules

  • The queue defines charge behavior only (not the initial checkout payment).

  • If the queue has fewer items than the number of cycles, all remaining charges default to success.

  • If no chargeEmitter is provided, all charges default to success.

Key points to remember

  • If chargeEmitterQueue is not provided, all charges are assumed successful.

  • You can use this feature to test retry logic, dunning flows, and webhook handling without needing special test cards.

  • Make sure your webhook implementation is idempotent, since rejected charges will trigger failure events you may want to retry.

Request Body Sample
{
  "customerKey": "{{customerKey}}",
  "subscriptionName": "Premium Plan",
  "endDate": "2025-08-21T19:45:00Z",
  "productInfo": {
    "orderId": "bff0757d-5aee-406a-b165-461cce42f349",
    "description": "My product"
  },
  "pricingInfo": {
    "totalAmount": 99.98,
    "currency": "USD",
    "pricePerUnit": 49.99,
    "quantity": 2
  },
  "billingCycle": {
    "intervalType": "Daily",
    "intervalCount": 1
  },
  "webhookUrl": "https://webhook.example.com/subscription/callback"
}

Response

{
    "result": {
        "membershipKey": "mmbrsp_cc0067638c2240999c53824a45db0360"
    }
}

Get Membership by Key

GET /api/subscriptions/v1/membership/{membershipKey}

Retrieves the full details of a previously created membership.

Use cases:

  • Fetch membership information to display in your UI (e.g., plan details, pricing, status).

  • Verify that a membership exists and check its current state (active, cancelled, expired) before performing further actions.

Request Headers

Name
Value
Required

Authorization

Bearer <token>

Response

{
    "result": {
        "key": "mmbrshp_3cf7488e4c4545a998634c664161665a",
        "customerKey": "cstmr_737120202a0241888b307766308cb330",
        "name": "Premium Plan",
        "status": "Incomplete",
        "settings": {
            "intervalType": "Daily",
            "intervalCount": 1
        },
        "invoices": [],
        "createdDate": "2025-08-21T10:08:11.684394+00:00",
        "updatedDate": null,
        "activationDate": null,
        "endDate": "2027-08-21T19:45:00+00:00",
        "nextRenewalDate": null,
        "orderId": "bff0757d-5aee-406a-b165-461cce42f349",
        "productDescription": "My product",
        "totalAmount": 99.98,
        "pricePerUnit": 49.99,
        "quantity": 2,
        "currency": "USD"
    }
}

Cancel Membership

POST /api/subscriptions/v1/membership/cancel/{membershipKey}

Cancels an active membership immediately. After cancellation:

  • No new billing cycles will be started.

  • Any open invoices (not yet finalized/paid) will be automatically transitioned to status Void.

  • Already paid or failed invoices remain unchanged.

Use cases

  • Let customers stop their subscription through your UI (e.g., “Cancel Subscription” button).

  • Programmatically end a trial or paid membership when business rules require.

  • Safely retry cancel calls without risk of duplication or errors.

Request Headers

Name
Value
Required

Authorization

Bearer <token>

Response

{} // Empty object.

UI Sessions

Create Checkout Session

POST /api/subscriptions/v1/checkout/session

Creates a hosted checkout session for a specific customer and membership. It returns a redirect URL where the customer completes identity confirmation and payment.

How it works

  1. Your backend calls this endpoint with a valid customerKey and membershipKey.

  2. The API returns checkoutRedirectUrl.

  3. You redirect the customer’s browser to that URL.

  4. The customer completes checkout on Paytiko’s hosted page.

  5. Your system is notified via webhooks configured on the membership (e.g., payment succeeded/failed), and the customer receives an invoice email.

Use cases

  • Start the initial payment for a newly created membership.

  • Re-issue a checkout when a previous session expired.

  • Provide a pay now link from your app or email.

Request Headers

Name
Value
Required

Content-Type

application/json

Authorization

Bearer <token>

Request Body

Name
Type
Required
Description

customerKey

string

Customer identifier.

Must start with cstmr_.

membershipKey

string

Membership identifier.

Must start with mmbrshp_.

returnToWebsiteLink

string (URL)

Optional absolute return URL. A full URL where the customer is redirected after leaving the portal. Must be a valid absolute URL (e.g., https://example.com/return).

Request Body Sample
{
  "customerKey": "{{customerKey}}",
  "membershipKey": "{{membershipKey}}",
  "returnToWebsiteLink": "https://app.example.com/account/billing"
}

Response

{
    "result": {
        "checkoutRedirectLink": "{checkoutBaseUrl}/daf8f4f7-5a4d-4b9e-9fc3-959781c226b3"
    }
}

Create Portal Session

POST /api/subscriptions/v1/checkout/session

Creates a customer portal session URL. The portal is a hosted, self-service page where customers can view and manage their subscriptions (e.g., see plan details, invoices, update payment method—exact features depend on your configuration).

How it works

  1. Your backend calls this endpoint with the customer’s key.

  2. The API returns portalRedirectUrl.

  3. You redirect or link the customer to this URL to self-manage their billing profile.

Common use cases

  • Provide a “Manage subscription” link in your app.

  • Let customers view invoices or update payment details without building UI yourself.

  • Offer a self-service experience that reduces support workload.

Request Headers

Name
Value
Required

Content-Type

application/json

Authorization

Bearer <token>

Request Body

Name
Type
Required
Description

customerKey

string

Customer identifier.

Must start with cstmr_.

returnToWebsiteLink

string (URL)

Optional absolute return URL. A full URL where the customer is redirected after leaving the portal. Must be a valid absolute URL (e.g., https://example.com/return).

Request Body Sample
{
  "customerKey": "{{customerKey}}",
  "returnToWebsiteLink": "https://app.example.com/account/billing"
}

Response

{
    "result": {
        "portalRedirectLink": "{portalBaseUrl}/f1092c5c-b384-4058-aedb-6b9350fc58a7"
    }
}

Webhooks

Overview

Paytiko sends webhooks to notify your system about important subscription events. There are two categories of webhook events:

  1. Transaction status updates — covers payments, refunds, charge attempts, etc.

    • In the webhook payload, the Action field will be set to:

      "TRANSACTION_DATA_UPDATE"
    • Full reference for transaction webhooks can be found here.

  2. Membership status updates — covers subscription lifecycle changes (e.g., activation, past due, cancellation).

    • In the webhook payload, the Action field will be set to:

      "SUBSCRIPTION_STATUS_CHANGE"
    • Detailed documentation for membership webhooks is provided below in this section.


Membership Status Update Webhook

What is it? A webhook sent whenever a membership (subscription) changes status. The payload includes the updated membership object and recent invoices for context.

How it works

  • Your webhookUrl (set during Create Membership) receives an HTTP POST with JSON.

  • The Status field in the payload reflects the new, updated subscription status.

  • You should update your local records and trigger any downstream logic (access enable/disable, emails, etc.).

Possible membership statuses

  • Active

  • PastDue

  • Unpaid

  • IncompleteExpired

  • Canceled

  • Completed

Only the status changes between events; the rest of the schema remains the same.

Delivery & Retries

Paytiko attempts to deliver each webhook event by sending an HTTP POST request to the merchant’s configured webhookUrl.

  • If your endpoint responds with an HTTP 2xx status code, the delivery is considered successful.

  • If your endpoint responds with a non-2xx status (e.g., 4xx, 5xx) or times out, the system will retry delivery.

Retry policy

  • Each webhook delivery has 1 initial attempt plus up to 6 retry attempts (7 attempts total).

  • A custom backoff schedule is applied:

    • The initial attempt is sent immediately.

    • The first retry is delayed by 5 minutes.

    • The second retry is delayed by 30 minutes.

    • Each subsequent retry doubles the delay (1 hour, 2 hours, 4 hours, 8 hours).

    Attempt #1 — Initial — Immediate Attempt #2 — Retry #1 — 5 minutes Attempt #3 — Retry #2 — 30 minutes Attempt #4 — Retry #3 — 1 hour Attempt #5 — Retry #4 — 2 hours Attempt #6 — Retry #5 — 4 hours Attempt #7 — Retry #6 — 8 hours

  • If all retry attempts fail, the event is marked as undeliverable and no further retries will be attempted.

  • All retry scheduling is based on UTC time, ensuring consistent behavior across time zones and daylight saving changes.

Best practices

  • Always return a 2xx response as quickly as possible — queue the work internally if needed.

  • Log incoming ActionId and Membership.Key values so you can reconcile missed events if a retry sequence is exhausted.

  • Keep your webhook endpoint highly available and resilient to ensure timely state synchronization.

Webhook Payload Structure Sample
{
  "Action": "SUBSCRIPTION_STATUS_CHANGE",
  "ActionId": "516e1e6f-ecca-4f45-a4d6-abaa14245668",
  "OrderId": "6a954f17-0eae-475c-9f51-8fc215abb512",
  "Membership": {
    "Id": 748,
    "Key": "mmbrshp_e68995caf18a4147ac730244d365d104",
    "CustomerKey": "cstmr_25ab3cc46c7a49c388e6a0bda78d669e",
    "Name": "Premium Plan",
    "Status": "Completed",
    "OrderId": "6a954f17-0eae-475c-9f51-8fc215abb512",
    "AccountId": 10155924,
    "MerchantId": 4,
    "CreatedDate": "2025-08-15T15:55:40.505067+00:00",
    "UpdatedDate": "2025-08-15T18:00:13.74996+00:00",
    "ActivationDate": "2025-08-15T15:56:17.35422+00:00",
    "ProductDescription": "Email Trigger Events and Displaying nd Date",
    "TotalAmount": 34,
    "Currency": "USD",
    "Quantity": 1,
    "PricePerUnit": 34,
    "InvoiceVector": [
      {
        "Id": 1186,
        "Key": "invc_8018d3df77f94e33bfd65950cda1edf3",
        "ReceiptKey": "rcpt_0102d50765244c13acdb89b7b8f352fc",
        "Amount": 34,
        "Currency": "USD",
        "Status": "Paid",
        "CreatedDate": "2025-08-15T15:56:08.825856+00:00",
        "UpdatedDate": "2025-08-15T15:56:17.331371+00:00",
        "Period": {
          "StartDate": "2025-08-15T15:56:08.825833+00:00",
          "EndDate": "2025-08-15T16:56:08.825833+00:00"
        },
        "Attempts": {
          "LastAttemptDate": null,
          "AttemptedRetryCount": 0
        },
        "PaymentMethod": {
          "PaymentMethodId": 252,
          "PaymentMethodMaskedPan": "411111******1111"
        }
      },
      {
        "Id": 1187,
        "Key": "invc_13a2a15000224a289905824a3029e1b2",
        "ReceiptKey": "rcpt_dcd689547af441a683f0fb98cc9d4088",
        "Amount": 34,
        "Currency": "USD",
        "Status": "Paid",
        "CreatedDate": "2025-08-15T17:00:26.566213+00:00",
        "UpdatedDate": "2025-08-15T17:00:32.562065+00:00",
        "Period": {
          "StartDate": "2025-08-15T16:56:17.35422+00:00",
          "EndDate": "2025-08-15T17:56:17.35422+00:00"
        },
        "Attempts": {
          "LastAttemptDate": "2025-08-15T17:00:26.558356+00:00",
          "AttemptedRetryCount": 0
        },
        "PaymentMethod": {
          "PaymentMethodId": 252,
          "PaymentMethodMaskedPan": "411111******1111"
        }
      }
    ]
  },
  "IssuedAt": "2025-08-15T18:00:13.7940023+00:00",
  "Signature": "f3d1d474f20f71e9aeea0dbfe2a3b6284f48f06a63ed40d83936eea11995e71b"
}

Payload Handling Recommendations

  • Respond with 2xx quickly. Process heavy work asynchronously.

  • Idempotency: use ActionId (or your own event ID map) to ignore duplicates.

  • Security: verify authenticity (e.g., shared secret/signature) if your integration provides one.

  • Audit: log Action, ActionId, Membership.Key, Membership.Status, and IssuedAt.

  • State sync: treat webhook as source of truth for membership state changes and update your DB accordingly.


Signature Verification

For security, every webhook request is signed so that your system can verify it originates from Paytiko and has not been tampered with.

How it’s generated

The signature is generated using SHA-256 hashing of the merchant’s secret key together with the membership’s OrderId.

Formula:

Signature = SHA256("{SecretKey}:{OrderId}")
  • SecretKey — your merchant’s private secret key (never exposed publicly).

  • OrderId — the order identifier associated with the membership or transaction.

Example

If your merchant secret key is:

sk_test_123456

and the OrderId in the webhook payload is:

6a954f17-0eae-475c-9f51-8fc215abb512

then the string to be hashed is:

sk_test_123456:6a954f17-0eae-475c-9f51-8fc215abb512

The resulting SHA-256 hash will be the signature value you should compare against the one provided in the webhook headers.

Validation Steps

  1. Extract the OrderId from the webhook payload.

  2. Concatenate your SecretKey and OrderId with a colon (:).

  3. Generate the SHA-256 hash of this string.

  4. Compare the result with the signature sent in the webhook header.

    • If they match → the webhook is authentic.

    • If not → reject the webhook.

IP Whitelisting

In addition to signature validation, you must also whitelist Paytiko’s official webhook source IP addresses in your firewall or API gateway.

Why this is required

  • Webhooks are sent over the public internet, and malicious actors could attempt to mimic Paytiko’s payload format.

  • Even if a payload looks correct, if it does not come from Paytiko’s trusted infrastructure, it must be rejected.

  • IP whitelisting ensures your server accepts webhook traffic only from Paytiko’s infrastructure.

How it works

  • Paytiko will provide a list of static outbound IP addresses.

  • Your system must configure these IPs as allowed sources for webhook requests.

  • Requests from any other IP must be blocked before signature validation is even attempted.

FAQ

❓ How are customers identified?

Customers are uniquely identified by email within the scope of a single merchant. Creating a customer with the same email is idempotent — the API will return the existing customerKey instead of creating a duplicate.

❓ How are memberships identified?

Memberships are uniquely identified by orderId. Creating a membership with the same orderId is also idempotent — the API will return the existing membership instead of creating a duplicate.

❓What happens if I don’t provide endDate for a membership?

If endDate is omitted, the membership will be infinite and continue until it is explicitly cancelled. If provided, it must be at least 1 hour ahead of current UTC time.

❓ How is pricing validated?
  • totalAmount must equal pricePerUnit × quantity (when both are provided).

  • All amounts must be greater than 0.

  • Currency must be a valid 3-letter ISO code (e.g., USD, EUR).

❓ How does billing cycle calculation work?
  • intervalType defines the base recurrence unit (Daily, Weekly, Monthly, Annually).

  • intervalCount multiplies the base interval (default = 1 if omitted).

    • Example: intervalType=Weekly, intervalCount=2 → every 2 weeks.

  • Edge cases: For end-of-month memberships, if the current month has fewer days, the system will always bill on the last available day of the month.

❓ Can memberships be cancelled at the end of the billing cycle instead of immediately?

No. The Cancel Membership endpoint always cancels immediately. If you want “cancel at end of period” behavior, you must track that in your system and call the API when the period ends.

❓ What happens to invoices when a membership is cancelled?
  • Open invoices → automatically marked as Void.

  • Paid invoices → remain unchanged.

  • Failed invoices → remain unchanged and will not be retried.

❓ How does webhook delivery work?
  • By webhookUrl provided, the system will send event notifications (e.g., payment success/failure).

  • The URL must be a valid absolute HTTPS URL.

  • Merchants should implement idempotency in their webhook handlers, as the same event may be retried if delivery fails.

❓ Is the API idempotent?

Only the following API endpoints are idempotent:

  • Creating customers by email is idempotent.

  • Creating memberships by orderId is idempotent.

❓ What format is required for phone numbers?
  • Example: +48123456789

  • Must contain 8–15 digits, no spaces or formatting characters.

❓ What test data should I use?
  • Use fake but valid-looking data (emails, phone numbers, addresses).

  • For phone, always use a non-existent but valid E.164 number (e.g., +48111222333).

  • For webhook URLs, use test endpoints such as https://webhook.site/....

Last updated

Was this helpful?