Integrate Subscriptions

FasterPay allows you to create subscriptions for your customers and charge them on a recurring basis. This includes features such as monthly billing, free trials, and canceling subscriptions via API.


Prerequisites

To integrate Subscriptions you will need your API keys which you can find in your FasterPay Business account. If you don’t have a FasterPay Business account yet, you can sign up for one.

You can start testing the API and follow the steps below using the Test Mode and Go Live after.


How do Subscriptions work?

  1. The customer clicks on the Pay Button on your website and is redirected to the FasterPay checkout page.
  2. The customer makes a payment and sets up a subscription with your website’s services. FasterPay then sends a Pingback to your servers.
  3. The customer is then redirected to your Thank You page.
  4. FasterPay will then charge the customer on a pre-agreed recurring basis. For each scheduled payment, your servers will receive a Pingback.
FasterPay subscription integration

How to integrate Subscriptions

To integrate Subscriptions into your FasterPay Business account follow the steps below:

Step 1: Redirect your customer to the Checkout Page

Once your customer wants to pay, redirect them to FasterPay Checkout Page using the code below:

<?phprequire_once('path/to/fasterpay-php/lib/autoload.php');

$gateway = new FasterPay\Gateway([
    'publicKey' => '<your-public-key>',
    'privateKey' => '<your-private-key>',
    'isTest' => 1,
]);
$form = $gateway->paymentForm()->buildForm(
    [
        'description' => 'Test order',
        'amount' => '10',
        'currency' => 'USD',
        'merchant_order_id' => time(),
        'success_url' => 'https://yourcompanywebsite.com/success',
        'recurring_name' => 'recurring1',
        'recurring_sku_id' => 'recurring1',
        'recurring_period' => '6m',
        'recurring_trial_amount' => '30',
        'recurring_trial_period' => '1m',
        'pingback_url' => 'https://yourcompanywebsite.com/pingback',
        'sign_version' => 'v2' // to use version 1 please skip this param or set it 'v1'
    ],
    [
        'autoSubmit' => false,
        'hidePayButton' => false
    ]
);
echo $form;
from fasterpay.gateway import Gateway
from random import randint

if __name__ == "__main__":

    gateway = Gateway("<your private key>", "<your public key>")

    parameters = {
        "payload": {
            "description": "Golden Ticket",
            "amount": "10",
            "currency": "EUR",
            "merchant_order_id": randint(1000, 9999),
            "success_url": "http://localhost:12345/success.php",
            "recurring_name": "Test FP Recurring",
            "recurring_sku_id": "fp_test_recurring",
            "recurring_period": "10d",
            "recurring_trial_amount": "1",
            "recurring_trial_period": "3d",
            "recurring_duration": 0
        },
        "auto_submit_form": True
    }

    paymentForm = gateway.payment_form().build_form(parameters)

    print paymentForm
const fasterpay = require('fasterpay-node');

let gateway = new fasterpay.Gateway({
    publicKey: '<your public key>',
    privateKey: '<your private key>',
    isTest: 1 // Use 1 for Test Method
});

app.get('/subscribe', (req, res) => {
	let paymentForm = gateway.PaymentForm().buildForm({
        'description': 'Test order',
        'amount': '0.10',
        'currency': 'USD',
        'merchant_order_id': new Date().getTime().toString(),
        'sign_version': 'v2',
        'recurring_name': 'recurring1',
        'recurring_sku_id': 'recurring1',
        'recurring_period': '6m',
        'recurring_trial_amount': '0.10',
        'recurring_trial_period': '1m',
        'recurring_duration': '0',
        'success_url': baseurl + '/success',
        'pingback_url': baseurl + '/pingback'
	},{
		'autoSubmit': false,
		'hidePayButton': false,
	});
    res.send(paymentForm);
});

public class TransactionServlet extends HttpServlet {

    private Gateway gateway = Gateway.builder()
        .publicApi("<your public key>")
        .privateApi("<your private key>")
        .build();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        PrintWriter writer = resp.getWriter();

        Form subscriptionsForm = gateway.subscriptionForm()
            .amount("1")
            .currency("USD")
            .description("Moonsoon festival")
            .merchantOrderId(UUID.randomUUID().toString())
            .recurringName("moonsoon")
            .recurringSkuId("festival")
            .recurringPeriod("3m")
            .sign_version(SignVersion.VERSION_2)
            .isAutoSubmit(true);

        writer.println("<html><body>");
        writer.println(subscriptionsForm.build());
        writer.println("</body></html>");
    }
}
class MainActivity : AppCompatActivity() {

    val fasterPay: FasterPay by lazy {
        FasterPay("<your public key>", true)
    }

    private fun subscriptions() {
        val subscriptionsForm = gateWay.subscription()
            .amount("5")
            .currency("USD")
            .description("Test product description")
            .merchantOrderId(UUID.randomUUID().toString())
            .successUrl("http://fasterpay.datph")
            .recurringName("Test FP Recurring")
            .recurringSkuId("fp_test_recurring")
            .recurringPeriod("10d")
            .recurringTrialAmount("1")
            .recurringTrialPeriod("3d")

        startActivity(gateWay.prepareCheckout(this, subscriptionsForm))
    }
}
curl https://pay.fasterpay.com/payment/form \
-d "api_key: [YOUR_PUBLIC_KEY]" \
-d "merchant_order_id=w2183261236" \
-d "amount=9.99" \
-d "currency=USD" \
-d "email=user@host.com" \
-d "description=Golden Ticket" \
-d "recurring_name=Golden Ticket Subscriptions" \
-d "recurring_sku_id=SKU ID / Product ID" \
-d "recurring_period=3m" \
-d "hash=<hash generated using the combination of post params and merchant private key>"

You can find more information on the API parameters in the Subscription API.


Step 2: Process the Pingback

After the customer makes a payment, FasterPay will send a Pingback to your servers notifying you about the payment.

You can use the code below to validate the Pingback, track the payment in your server and deliver the product:

<?phprequire_once('path/to/fasterpay-php/lib/autoload.php');
$gateway = new FasterPay\Gateway([
    'publicKey' => '<your-public-key>',
    'privateKey' => '<your-private-key>',
    'isTest' => 1,
]);
$signVersion = FasterPay\Services\Signature::SIGN_VERSION_1;
if (!empty($_SERVER['HTTP_X_FASTERPAY_SIGNATURE_VERSION'])) {
    $signVersion = $_SERVER['HTTP_X_FASTERPAY_SIGNATURE_VERSION'];
}
$pingbackData = null;
$validationParams = [];
switch ($signVersion) {
    case FasterPay\Services\Signature::SIGN_VERSION_1:
        $validationParams = ["apiKey" => $_SERVER["HTTP_X_APIKEY"]];
        $pingbackData = $_REQUEST;
        break;
    case FasterPay\Services\Signature::SIGN_VERSION_2:
        $validationParams = [
            'pingbackData' => file_get_contents('php://input'),
            'signVersion' => $signVersion,
            'signature' => $_SERVER["HTTP_X_FASTERPAY_SIGNATURE"],
        ];
        $pingbackData = json_decode(file_get_contents('php://input'), 1);
        break;
    default:
        exit('NOK');
}
if (empty($pingbackData)) {
    exit('NOK');
}
if (!$gateway->pingback()->validate($validationParams)) {
    exit('NOK');
}
#TODO: Write your code to deliver contents to the End-User.
echo "OK";
exit();
from flask import request
from fasterpay.gateway import FP_Gateway
gateway = Gateway("<your private key>", "<your public key>")
if gateway.pingback().validate({"apiKey": request.headers.get("X-ApiKey")}) is True:
  print "OK"
else:
  print "NOK"
const fasterpay = require('fasterpay-node');

let gateway = new fasterpay.Gateway({
    publicKey: '<your public key>',
    privateKey: '<your private key>',
    isTest: 1 // Use 1 for Test Method
});

app.post("/pingback", (req, res) => {
    let html = 'NOK';
    if(gateway.Pingback().validate(req)){
        html = 'OK';
    }
    res.send(html);
});

import spark.Service;

public class PingBackRoute implements Routes {

    private PingBack pingBack = new PingBack("<your private key");

    @Override
    public void define(Service service) {
        this.service = service;
        definePingbackRoutes();
    }

    @POST
    public void definePingbackRoutes() {
        service.post(BASE_PATH, ((request, response) -> {
            boolean isValid = pingBack.validation()
                .signVersion(Optional.ofNullable(request.headers(PingBack.X_FASTERPAY_SIGNATURE_VERSION)))
                .apiKey(Optional.ofNullable(request.headers(PingBack.X_API_KEY)))
                .signature(Optional.ofNullable(request.headers(PingBack.X_FASTERPAY_SIGNATURE)))
                .pingBackData(Optional.of(request.body()))
                .execute();
            if (isValid) {
                //process further with pingBack
                response.status(HttpStatus.OK_200);
                return response;
            } else {
                //process with invalid pingBack
                throw halt(HttpStatus.NOT_IMPLEMENTED_501);
            }
        }));
    }
}
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{ \
    "event":"payment", \
    "payment_order":{ \
      "id":273281973, \
      "merchant_order_id":"w2981398127", \
      "payment_system":1, \
      "status":"successful", \
      "paid_amount":46.049999999999997, \
      "paid_currency":"EUR", \
      "merchant_net_revenue":42.227200000000003, \
      "merchant_rolling_reserve":2.3025000000000002, \
      "fees":1.5203, \
      "date":{ \
        "date":"2018-11-30 23:59:10.000000", \
        "timezone_type":3, \
        "timezone":"UTC" \
      } \
    }, \
    "user":{ \
      "firstname":"John", \
      "lastname":"Doe", \
      "username":"John-Doe@my.passport.io", \
      "country":"US", \
      "email":"johndoe@example.com" \
    }, \
    "with_risk_check":false, \
  }'
 https://your-pingback-url.com/pingback.php

FasterPay expects the following response:

Response Status: 200
Response Body: ok

In case a different response is received, FasterPay will resend Pingbacks every 30 minutes for up to 10 times.

You can also accept a range of Pingback types, such as refunds and chargebacks.

More about Pingbacks.


Step 3: Show your Thank You page.

Once the user makes a payment, they will be redirected to the success_url page that you specify during step.1. You can show your “Thank You” message on this screen.


Step 4: Process subscription Pingbacks.

FasterPay will send Pingbacks for the following events:

Recurring payment

When a customer is automatically charged for the subscription, FasterPay will send the pingback below:

{
  "event": "payment",
  "payment_order": {
   "id": 1005002001,
   "merchant_order_id": "w146138485",
   "status": "successful",
   "paid_amount": 5,
   "paid_currency": "USD",
   "merchant_net_revenue": 4.23,
   "merchant_rolling_reserve": 0.25,
   "fees": 0.52,
   "date": {
    "date": "2018-04-25 12:15:07.000000",
    "timezone_type": 3,
    "timezone": "UTC"
   }
 },
 "user": {
  "firstname": "John",
  "lastname": "Smith",
  "username": "john-smith@my.passport.io",
  "country": "TR",
  "email": "john.smith@email.com"
 },
 "subscription": {
    "id": 100345508,
    "u_email": "john.smith@email.com",
    "recurring_id": 1005002001,
    "status": "Trial",
    "cancel_delay": 0,
    "currency_code": "USD",
    "trial": 1,
    "trial_price": "5.00",
    "trial_period_length": 3,
    "trial_period_type": "day",
    "counter": 1,
    "limit": 0,
    "failures": 0,
    "period_length": 10,
    "period_type": "day",
    "billing_type": 1,
    "price": "105.00",
    "date_active": 0,
    "date_start": 1533704765,
    "date_next": 1533963965,
    "date_end": 1565067965,
    "date_paid": 1533704765,
    "date_expired": 0,
    "date_cancelled": 0,
    "date_stopped": 0,
    "created_at": 1533704765,
    "package_version_id": 213,
    "change_request": null,
    "package": {
      "id": 0,
      "sku_id": "your_product_sku_id",
      "name": "your product name",
      "currency_code": "USD",
      "period_length": 10,
      "period_type": "day",
      "recurring_limit": 0,
      "trial": 1,
      "trial_price": "5.00",
      "trial_period_length": 3,
      "trial_period_type": "day",
      "price": "105.00",
      "discount_price": null,
      "active": 1,
      "subscriptions_count": 1
    },
  "paid_number_counter": 1,
  "init_merchant_order_id": "w146138485"
 },
 "pingback_ts": 1562842637
}

Subscription is stopped due to failed payments

If a recurring payment fails (for example due to insufficient funds), FasterPay will try to charge the customer again in 6 hours for up to 3 times. If the payment goes through, you will receive a recurring payment pingback. If all of the attempts fail, the subscription will be stopped and you will get the pingback below.

{
  "event": "subscription_stopped",
  "subscription": {
    "id": 100345508,
    "u_email": "john.smith@email.com",
    "recurring_id": 1005002001,
    "status": "Cancelled",
    "cancel_delay": 0,
    "currency_code": "USD",
    "trial": 1,
    "trial_price": "5.00",
    "trial_period_length": 3,
    "trial_period_type": "day",
    "counter": 1,
    "limit": 0,
    "failures": 0,
    "period_length": 10,
    "period_type": "day",
    "billing_type": 1,
    "price": "105.00",
    "date_active": 0,
    "date_start": 1533704765,
    "date_next": 1533963965,
    "date_end": 1565067965,
    "date_paid": 1533704765,
    "date_expired": 0,
    "date_cancelled": 1533704799,
    "date_stopped": 0,
    "created_at": 1533704765,
    "package_version_id": 213,
    "change_request": null,
    "package": {
      "id": 0,
      "sku_id": "your_product_sku_id",
      "name": "your product name",
      "currency_code": "USD",
      "period_length": 10,
      "period_type": "day",
      "recurring_limit": 0,
      "trial": 1,
      "trial_price": "5.00",
      "trial_period_length": 3,
      "trial_period_type": "day",
      "price": "105.00",
      "discount_price": null,
      "active": 1,
      "subscriptions_count": 1
    },
    "paid_number_counter": 1
  },
  "pingback_ts": 1562842637
}

Subscription is cancelled

If a subscription is cancelled (for example by the customer), FasterPay we will send the pingback below:

{
  "event": "subscription_cancelled",
  "subscription": {
    "id": 100345508,
    "u_email": "john.smith@email.com",
    "recurring_id": 1005002001,
    "status": "Cancelled",
    "cancel_delay": 0,
    "currency_code": "USD",
    "trial": 1,
    "trial_price": "5.00",
    "trial_period_length": 3,
    "trial_period_type": "day",
    "counter": 1,
    "limit": 0,
    "failures": 0,
    "period_length": 10,
    "period_type": "day",
    "billing_type": 1,
    "price": "105.00",
    "date_active": 0,
    "date_start": 1533704765,
    "date_next": 1533963965,
    "date_end": 1565067965,
    "date_paid": 1533704765,
    "date_expired": 0,
    "date_cancelled": 1533704799,
    "date_stopped": 0,
    "created_at": 1533704765,
    "package_version_id": 213,
    "change_request": null,
    "package": {
      "id": 0,
      "sku_id": "your_product_sku_id",
      "name": "your product name",
      "currency_code": "USD",
      "period_length": 10,
      "period_type": "day",
      "recurring_limit": 0,
      "trial": 1,
      "trial_price": "5.00",
      "trial_period_length": 3,
      "trial_period_type": "day",
      "price": "105.00",
      "discount_price": null,
      "active": 1,
      "subscriptions_count": 1
    },
    "paid_number_counter": 1
  },
  "pingback_ts": 1562842637
}

Step 5: Integrate Delivery Confirmation API

For every product purchased by an end-user, merchants that don’t instantly deliver the products are required to implement FasterPay Delivery Confirmation API and communicate the delivery status of the product / service.

This information is important for our customer service teams in order to investigate disputes raised by end-users in a better way.

Requirements for E-commerce businesses

As a general rule, card schemes (e.g. Visa, MasterCard) do not allow capturing a transaction until goods have been shipped or services rendered.

When a user makes a purchase on your E-commerce store, FasterPay only authorizes the transaction. You are expected to ship the goods within 7 days of handling time. If an order is not shipped within 7 days - the transaction is automatically voided and the funds are returned to the user.

A proof of shipping should be communicated to FasterPay automatically via Delivery Confirmation API with status=order_shipped along with tracking ID, tracking link and carrier name. Upon receiving the confirmation, FasterPay captures the payment and the funds move to your Pending Balance.

Instructions can be found in the APIs section here: Delivery Confirmation API

If you are using FasterPay’s plugins in your E-Commerce stores, please refer to respective plugin documentation on configuring Delivery Confirmation.


Step 6: Manage subscriptions via API.

If needed, you can manage subscriptions via API, for example when you need to cancel a subscription. Alternatively, you can use Subscription Management Tools in your FasterPay Business Account.


Step 7: Test.

Once you have completed implementation, go ahead and test it using the Test Credit Card numbers found in FasterPay’s Test Mode. You can turn on test mode by clicking the toggle in the header.


Step 8: Go Live.

Once you’re done, simply fill out your Business Profile and Go Live!