SubscriptionPro Merchant API Docs
Integration Guide · v1.0

Merchant API Integration Guide

Build a Merchant API that connects your product to SubscriptionPro. Once integrated, customers can buy your product through SubscriptionPro and licenses are generated automatically.

Last updated: April 26, 2026

How It Works (Big Picture)

The full purchase-to-license flow between the customer, SubscriptionPro, and your Merchant API:

Loading diagram…

In simple terms:

  1. Customer clicks “Buy” on SubscriptionPro
  2. Customer is redirected to your website to authorize access
  3. After authorization, SubscriptionPro gets a token from your API
  4. After payment, SubscriptionPro calls your API to generate a license
  5. The license is shown to the customer in their order dashboard

What You Need to Build

Your API needs these 8 endpoints. Here's exactly what each one does.

# Method Endpoint Purpose
1GET/api/authorizeShow authorization page to customer
2GET/api/authorize/confirmCustomer clicks “Authorize”, redirect back with code
3POST/api/tokenExchange auth code for access token
4POST/api/license/activateGenerate a license after payment
5POST/api/license/validateCheck if a license key is valid
6GET/api/profileGet customer profile
7GET/api/billingGet billing history
8POST/api/cancelCancel subscription and revoke license
Tip The paths after /api/ can be customized. You'll configure them in the SubscriptionPro merchant dashboard when setting up your product.

Step 1 · Database Tables

You'll need these four tables in your database.

customers — Stores customer data from SubscriptionPro

CREATE TABLE customers (
    id                 BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name               VARCHAR(255),
    email              VARCHAR(255) UNIQUE,
    access_token       VARCHAR(255) UNIQUE NULL,
    product_identifier VARCHAR(255) NULL,
    sp_customer_id     BIGINT UNSIGNED NULL COMMENT 'Customer ID from SubscriptionPro',
    created_at         TIMESTAMP NULL,
    updated_at         TIMESTAMP NULL
);

oauth_codes — Temporary authorization codes

CREATE TABLE oauth_codes (
    id                 BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    code               VARCHAR(255),
    client_id          VARCHAR(255) DEFAULT 'subscriptionpro',
    product_identifier VARCHAR(255) NULL,
    customer_id        BIGINT UNSIGNED NULL,
    used               BOOLEAN DEFAULT FALSE,
    expires_at         TIMESTAMP NULL,
    created_at         TIMESTAMP NULL,
    updated_at         TIMESTAMP NULL
);

licenses — License keys generated for customers

CREATE TABLE licenses (
    id                 BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    customer_id        BIGINT UNSIGNED,
    plan_name          VARCHAR(255),
    license_key        VARCHAR(255) UNIQUE,
    product_identifier VARCHAR(255),
    status             VARCHAR(50) DEFAULT 'active' COMMENT 'active, cancelled, expired, revoked',
    activated_at       TIMESTAMP NULL,
    expires_at         TIMESTAMP NULL,
    created_at         TIMESTAMP NULL,
    updated_at         TIMESTAMP NULL
);

billing_records — Payment history

CREATE TABLE billing_records (
    id             BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    customer_id    BIGINT UNSIGNED,
    plan_name      VARCHAR(255),
    amount         DECIMAL(10,2),
    transaction_id VARCHAR(255),
    status         VARCHAR(50) DEFAULT 'paid',
    billed_at      TIMESTAMP NULL,
    created_at     TIMESTAMP NULL,
    updated_at     TIMESTAMP NULL
);

Step 2 · Build Your Endpoints

GET/api/authorize

2.1 · Authorization Page

SubscriptionPro redirects the customer's browser to this URL. You show a page asking them to authorize access.

Query Parameters

ParameterTypeDescription
product_identifierstringYour product's unique ID in SubscriptionPro
redirect_uristringURL to redirect back to after authorization

What to do

  1. Show an HTML page with your branding
  2. Display what permissions SubscriptionPro is requesting
  3. Include an “Authorize” button that submits to /api/authorize/confirm
  4. Include a “Deny” button that redirects back to redirect_uri

Example response: Return an HTML page (not JSON).

GET/api/authorize/confirm

2.2 · Confirm Authorization

Called when the customer clicks “Authorize”. You generate a one-time code and redirect back to SubscriptionPro.

Query Parameters: Same as above (product_identifier, redirect_uri).

What to do

  1. Generate a random code (e.g. 40 characters)
  2. Save it to oauth_codes with used = false and expires_at = now + 10 minutes
  3. Redirect the customer to: {redirect_uri}?code={code}&product_identifier={product_identifier}

Example (PHP / Laravel)

public function authorizeConfirm(Request $request)
{
    $code = Str::random(40);

    OauthCode::create([
        'code'               => $code,
        'product_identifier' => $request->product_identifier,
        'used'               => false,
        'expires_at'         => now()->addMinutes(10),
    ]);

    $separator = str_contains($request->redirect_uri, '?') ? '&' : '?';

    return redirect(
        $request->redirect_uri . $separator
        . "code={$code}&product_identifier={$request->product_identifier}"
    );
}
POST/api/token

2.3 · Token Exchange

SubscriptionPro's backend calls this to exchange the one-time code for a long-lived access token.

Request Body (JSON)

{
  "code": "abc123xyz...",
  "product_identifier": "your-product-id",
  "customer_id": 42
}

What to do

  1. Look up the code in oauth_codes — check it's not used, not expired
  2. Mark the code as used = true
  3. Create or update a customer record with a new access_token
  4. Return the token

Response (JSON)

{
  "access_token": "your_generated_token_here",
  "refresh_token": "optional_refresh_token",
  "token_type": "Bearer",
  "customer_id": 1,
  "expires_in": 31536000
}
Important The access_token is stored by SubscriptionPro and used for all future API calls. Make it long-lived (1 year recommended).
POST/api/license/activateMost Important

2.4 · License Activation

Called automatically by SubscriptionPro after a successful payment. This is where you generate the license key.

Request Body (JSON)

{
  "access_token": "the_customers_token",
  "plan_name": "Monthly Plan",
  "product_identifier": "your-product-id",
  "amount": 9.99
}

What to do

  1. Validate the access_token — find the customer
  2. Generate a unique license key
  3. Save it to licenses with status = active
  4. Return the license details

Response (JSON)

{
  "success": true,
  "license_key": "ABCDE-FGHIJ-KLMNO-PQRST",
  "plan_name": "Monthly Plan",
  "status": "active",
  "activated_at": "2026-04-26T12:00:00.000000Z",
  "expires_at": "2027-04-26T12:00:00.000000Z"
}
Caution If this endpoint fails or returns an error, the customer's order will be created without a license. Make sure this endpoint is reliable.

Example (PHP / Laravel)

public function licenseActivate(Request $request)
{
    $customer = Customer::where('access_token', $request->access_token)->first();

    if (!$customer) {
        return response()->json(['error' => 'Invalid access token'], 401);
    }

    // Generate a unique license key (format: XXXXX-XXXXX-XXXXX-XXXXX)
    $licenseKey = strtoupper(implode('-', str_split(Str::random(20), 5)));

    $license = License::create([
        'customer_id'        => $customer->id,
        'plan_name'          => $request->plan_name,
        'license_key'        => $licenseKey,
        'product_identifier' => $request->product_identifier,
        'status'             => 'active',
        'activated_at'       => now(),
        'expires_at'         => now()->addYear(), // or addMonth() for monthly
    ]);

    return response()->json([
        'success'      => true,
        'license_key'  => $licenseKey,
        'plan_name'    => $request->plan_name,
        'status'       => 'active',
        'activated_at' => $license->activated_at->toISOString(),
        'expires_at'   => $license->expires_at->toISOString(),
    ]);
}
POST/api/license/validate

2.5 · License Validation

Checks if a license key is valid. Can be called by anyone with the key (no auth required).

Request Body

{
  "license_key": "ABCDE-FGHIJ-KLMNO-PQRST"
}

Response (valid)

{
  "valid": true,
  "license_key": "ABCDE-FGHIJ-KLMNO-PQRST",
  "status": "active",
  "plan_name": "Monthly Plan",
  "product_identifier": "your-product-id",
  "activated_at": "2026-04-26T12:00:00.000000Z",
  "expires_at": "2027-04-26T12:00:00.000000Z",
  "message": "License is valid and active."
}

Response (invalid)

{
  "valid": false,
  "message": "License key not found."
}
GET/api/profile

2.6 · Customer Profile

Returns customer info and their active license. Pass access_token as a Bearer header or query parameter.

Response

{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com",
  "product_identifier": "your-product-id",
  "sp_customer_id": 42,
  "active_license": {
    "license_key": "ABCDE-FGHIJ-KLMNO-PQRST",
    "plan_name": "Monthly Plan",
    "status": "active",
    "expires_at": "2027-04-26T12:00:00.000000Z"
  }
}
GET/api/billing

2.7 · Billing History

Returns all billing records for the customer.

Response

{
  "billing": [
    {
      "id": 1,
      "plan_name": "Monthly Plan",
      "amount": 9.99,
      "transaction_id": "TXN_ABC123",
      "status": "paid",
      "billed_at": "2026-04-26T12:00:00.000000Z"
    }
  ]
}
POST/api/cancel

2.8 · Cancel Subscription

Called when a customer cancels their subscription on SubscriptionPro. You should revoke their active licenses.

Request Body

{
  "access_token": "the_customers_token"
}

Response

{
  "success": true,
  "message": "Subscription cancelled successfully."
}

Step 3 · Configure Your Product in SubscriptionPro

When creating your product in the SubscriptionPro merchant dashboard, enable “API Integration” and fill in these URLs.

FieldWhat to enterExample
Webhook (Base URL)Your API's base URLhttps://api.yourproduct.com/api
Authorization URLPath for OAuth pageauthorize
License Activation URLPath for license generationlicense/activate
Payment URLPath for payment recordingpayment
Profile URLPath for customer profileprofile
Billing URLPath for billing historybilling
Cancel URLPath for cancellationcancel
Note The Webhook base URL is combined with each path. For example, if your webhook is https://api.yourproduct.com/api and the license activation URL is license/activate, SubscriptionPro will call https://api.yourproduct.com/api/license/activate.

Step 4 · Test Your Integration

Quick Test with cURL

1 · Get a Token (simulate)

curl -X POST http://your-api.com/api/token \
  -H "Content-Type: application/json" \
  -d '{"code":"test123", "product_identifier":"your-product-id", "customer_id": 1}'

2 · Activate a License

curl -X POST http://your-api.com/api/license/activate \
  -H "Content-Type: application/json" \
  -d '{
    "access_token": "TOKEN_FROM_STEP_1",
    "plan_name": "Monthly Plan",
    "product_identifier": "your-product-id",
    "amount": 9.99
  }'

3 · Validate the License

curl -X POST http://your-api.com/api/license/validate \
  -H "Content-Type: application/json" \
  -d '{"license_key": "LICENSE_KEY_FROM_STEP_2"}'

Error Handling

All endpoints should return proper HTTP status codes.

StatusMeaningWhen to use
200SuccessRequest completed successfully
400Bad RequestMissing or invalid parameters
401UnauthorizedInvalid or missing access token
404Not FoundResource not found (e.g. invalid license key)
500Server ErrorSomething went wrong on your end

Error response format

{
  "error": "Description of what went wrong"
}

Security Best Practices

Warning Follow these to keep your API secure.
  1. Always validate access tokens — never trust the token without checking your database
  2. Use HTTPS — all API calls should be over HTTPS in production
  3. Expire authorization codes quickly — 10 minutes maximum
  4. Mark codes as used — prevent code replay attacks
  5. Generate unique license keys — use cryptographically random strings
  6. Rate limit your endpoints to prevent abuse

Complete Laravel Starter

Want a head start? Scaffold the project, then drop in your routes.

# Create a new Laravel project
composer create-project laravel/laravel my-merchant-api
cd my-merchant-api

# Create your migrations
php artisan make:migration create_customers_table
php artisan make:migration create_oauth_codes_table
php artisan make:migration create_licenses_table
php artisan make:migration create_billing_records_table

# Create your models
php artisan make:model Customer
php artisan make:model OauthCode
php artisan make:model License
php artisan make:model BillingRecord

# Create the controller
php artisan make:controller MerchantController

Then add your routes in routes/api.php

use App\Http\Controllers\MerchantController;

Route::get('/authorize', [MerchantController::class, 'showAuthorizePage']);
Route::get('/authorize/confirm', [MerchantController::class, 'authorizeConfirm']);
Route::post('/token', [MerchantController::class, 'token']);
Route::post('/license/activate', [MerchantController::class, 'licenseActivate']);
Route::post('/license/validate', [MerchantController::class, 'licenseValidate']);
Route::get('/profile', [MerchantController::class, 'profile']);
Route::get('/billing', [MerchantController::class, 'billing']);
Route::post('/cancel', [MerchantController::class, 'cancel']);

Need Help?

Demo Merchant API

Check the demo source code for a complete working example.

Contact Support

Reach out to SubscriptionPro support for integration assistance.

Public Webhook

Make sure your Webhook base URL is accessible from the internet (not localhost).