Getting Started

You can use Bongloy accept online payments from your customers. Bongloy currently supports UnionPay credit, debit and prepaid cards. Integrating Bongloy into your app or website can begin as soon as you create a Bongloy Sandbox account, requiring only three steps:

  1. Obtain your API keys
  2. Install an API library so your integration can interact with the Bongloy API
  3. Make a test API request to confirm everything is up and running

Step 1: Obtain your API Keys

In order to get started with the API, you'll need to get your API Keys. Your test API keys can be found in the developer section on the Bongloy Sandbox. There are two types of API keys: publishable and secret.

  • Publishable API keys are only for identifying your account with Bongloy, they aren't secret. In other words, they can safely be published in places like your Bongloy.js JavaScript code, or in an Android or iPhone app.
  • Secret API keys should be kept confidential and only stored on your own servers. Your account's secret API key can perform any API request to Bongloy without restriction.
Before creating live charges, you'll need to create a live account and fill out the merchant application form. Once your account is approved, you'll have access to your live API keys.

Step 2: Install an API library

Bongloy's API is inspired by Stripe's REST API. Stripe is a US-based technology company which allows individuals and businesses to receive online payments. We thank Stripe for inspiring us to create a product which enables digital payments in Cambodia.

Where possible we try to make the Bongloy API compatible with Stripe's REST API. This makes integration easier because Stripe already has official libraries for different programming languages and mobile platforms. There's also bunch of pre-built extensions, platforms, and plugins to use Bongloy without needing to write any code.

We encourage you to use Stripe's official libraries, or third party libraries and plugins written for Stripe when connecting to Bongloy. This will ens ure that you receive the latest security updates and bug fixes.

Integrating a Stripe library or plugin with Bongloy varies according to the particular library. Below are some code snippits for some popular programming languages and platforms.

Ruby

# Gemfile
gem "stripe", github: "stripe/stripe-ruby"

# config/initializers/bongloy.rb
Bongloy = Stripe
Bongloy.api_base = "https://api.bongloy.com"

PHP

$ composer require stripe/stripe-php

<?php
  require_once('vendor/autoload.php');
  use Stripe As Bongloy;

  Bongloy\Stripe::$apiBase = "https://api.bongloy.com";
?>

Step 3: Make a test API request

To check that your integration is working correctly, make a test API request using your test secret key to create a charge. If the request is successful you should see the charge in your sandbox dashboard.

Ruby

# bongloy_test.rb

# replace the API key in the following line with your secret API key
Bongloy.api_key = "sk_test_abcde..."

charge = Bongloy::Charge.create(
  amount: 999,
  currency: "usd",
  source: "tok_union_pay"
)

PHP

<?php
  // bongloy_test.php

  // replace the API key in the following line with your secret API key
  Bongloy\Stripe::setApiKey("sk_test_abcde...");

  $charge = Bongloy\Charge::create([
    "amount" => 999,
    "currency" => "usd",
    "source" => "tok_union_pay"
  ]);
?>

Collecting cardholder data

Anyone involved with the processing, transmission, or storage of card data must comply with the] Payment Card Industry Data Security Standards (PCI DSS). Bongloy has been audited by an independent PCI Qualified Security Assessor (QSA) and is certified as a PCI Level 1 Service Provider. This is the most stringent level of certification available in the payments industry.

PCI compliance is a shared responsibility and applies to both Bongloy and your business. When accepting payments, you must do so in a PCI compliant manner. The simplest way for you to be PCI compliant is to never see (or have access to) card data at all. Bongloy makes this easy for you as we can do the heavy lifting to protect your customers' card information. You can simplify your PCI compliance as long as you:

  • Use Bongloy.js or Stripe's mobile SDK libraries to collect payment information, which is securely transmitted directly to Bongloy without it passing through your servers
  • Serve your payment pages securely using Transport Layer Security (TLS) so that they make use of HTTPS
  • Review and validate your account's PCI compliance annually

Bongloy.js

Bongloy.js makes it easy to collect credit card details without having the information touch your server. Bongloy.js gives you complete control over the UI and does not depend on JQuery. Use Bongloy.js if you want to accept payments on your website. If your building a mobile application then we recommend that you use Stripe's official libraries for iOS and Android.

Including Bongloy.js

Add the following script tag to your page to get started with Bongloy.js:

<script type="text/javascript" src="https://js.bongloy.com/v3"></script>

Setting your publishable key

You must set your publishable key with setPublishableKey before using Bongloy.js to identify your website when communicating with Bongloy. Remember to replace the test key with your live key in production. You can get all your keys from the developer section of the dashboard.

setPublishableKey also accepts an optional options object where you can specify a bongloyAccount, a connected account ID in which to perform actions on. Use this option when using Bongloy Connect to create tokens on behalf of another account. Note that when passing a bongloyAccount, you still use the publishable key of the platform application account, not that of the connected account.

Bongloy.setPublishableKey('pk_test_abcde...');
// Note: We could also get our Bongloy Publishable Key
// from a meta tag attribute in the HTML head. E.g.
// var publishableKey = document.head.querySelector("meta[name=bongloy-publishable-key]").content;
// Bongloy.setPublishableKey(publishableKey);
//
// When using Connect, you can set the bongloyAccount header to specify the
// connected account in which to perform actions on behalf of. For example:
// Bongloy.setPublishableKey('pk_test_abcde...', { bongloyAccount: "connected-account-id" });

Collecting card details

Bongloy.createToken

createToken converts sensitive card data to a single-use token which you can safely pass to your server to charge the user.

var cardObject = {
  // The HTML in this example uses `data-name` attribute
  // instead of the HTML name attribute to prevent
  // sending credit card information fields to the backend server via HTTP POST

  number:    document.querySelector('[data-name="cardNumber"]').value,
  exp_month: document.querySelector('[data-name="expMonth"]').value,
  exp_year:  document.querySelector('[data-name="expYear"]').value,
  cvc:       document.querySelector('[data-name="cvc"]').value
}

Bongloy.createToken('card', cardObject, bongloyResponseHandler);

The first argument to Bongloy.createToken is the type of token you want to create. For now this the value must be card.

The second argument to is a JavaScript object containing credit card data entered by the user. It should contain the following required fields:

number
Card number as a string without any separators, e.g. '4242424242424242'
exp_month
Two digit number representing the card's expiration month, e.g. 9
exp_year
Four digit number representing the card's expiration year, e.g. 2019
cvc
Card security code as a string, e.g. '123'

The following fields are entirely optional. They cannot result in a token creation failing:

name
Cardholder's name

The third argument bongloyResponseHandler is a callback you provide to handle the response from Bongloy. It should do the following:

  • If the card information entered by the user returned an error, display it on the page.
  • If no errors were returned (i.e. a single-use token was created successfully), add the returned token to the payment form and submit the form to your server.

Here's a sample implementation of bongloyResponseHandler:

function bongloyResponseHandler(status, response) {
  // hide error messages
  var errorMessages = document.querySelector('[data-name="errorMessages"]');
  errorMessages.classList.add('hidden');

  if (statusCode === 201) {
    // On success, set token in your checkout form
    document.querySelector('[data-name="cardToken"]').value = response.id;

    // Then, submit the form
    checkoutForm.submit();
  }
  else {
    // If unsuccessful, display an error message.
    // Note that `response.error.message` contains a preformatted error message.
    document.querySelector("input[type=submit]").removeAttribute('disabled');
    errorMessages.classList.remove('hidden');
    errorMessages.innerHTML = response.error.message;
  }
}

response is of the following form:

{
  "id": "af2a6c7c-11f4-4db1-bff6-ee991ebb41f1",
  "object": "token",
  "created": 1535543720,
  "livemode": false,
  "card": {
    "id": "b4199585-dc1c-4e06-9fdd-4261d703c6a1",
    "object": "card",
    "created": 1535543720,
    "livemode": false,
    "address_city": null,
    "address_country": null,
    "address_line1": null,
    "address_line1_check": "unchecked",
    "address_line2": null,
    "address_state": null,
    "address_zip": null,
    "address_zip_check": "unchecked",
    "brand": "visa",
    "country": null,
    "customer": null,
    "cvc_check": "pass",
    "exp_month": 8,
    "exp_year": 2020,
    "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
    "last4": "4242",
    "name": null
  },
  "client_ip": "96.9.66.131",
  "type": "card",
  "used": false
}

createToken is an asynchronous call. It returns immediately and invokes bongloyResponseHandler when it receives a response from Bongloy's servers.

Bongloy.js doesn't validate credit card data before sending it to the API. But if the card isn't valid the API will send you a message in the response containing the errors. If you need client side validation you can use something like jessepollak/payment.

Stripe's Mobile Libraries

Stripe has native libraries for Android and iOS which let you collect credit card information without having to deal with sensitive data passing through your servers. Stripe's Mobile Libraries are compatible with Bongloy. Below are some code snippits to get Stripe's Mobile Libraries working with Bongloy.

iOS

Install and configure the SDK

Here's an example of how to configure Stripe iOS with Bongloy and CocoaPods.

# Podfile
pod "Stripe"
$ pod install

Don't forget to use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file, from here on out.

To update to the latest version of the SDK, simply run:

$ pod update Stripe

Configure your Bongloy integration in your App Delegate

// AppDelegate.swift

import UIKit
import Stripe

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    STPPaymentConfiguration.shared().publishableKey = "pk_test_abcde..."
    // do any other necessary launch configuration
    return true
  }
}

Create a Bongloy API Client

// BongloyAPIClient.h

#import <Stripe/Stripe.h>

@interface BongloyAPIClient : STPAPIClient

@property (nonatomic, strong, readwrite) NSURL *apiURL;
@property (nonatomic, strong, readonly) NSURLSession *urlSession;
@property (nonatomic, strong, readwrite) NSString *apiKey;

- (instancetype)initWithConfiguration:(STPPaymentConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

@end

// BongloyAPIClient.m

#import "BongloyAPIClient.h"

static NSString * const APIBaseURL = @"https://api.bongloy.com/v1";

@implementation BongloyAPIClient

- (instancetype)initWithConfiguration:(STPPaymentConfiguration *)configuration {
  NSString *publishableKey = [configuration.publishableKey copy];
  self = [super initWithConfiguration:configuration];
  if (self) {
      _apiKey = publishableKey;
      _apiURL = [NSURL URLWithString:APIBaseURL];
      _urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
  }
  return self;
}
@end

Create a bridging header for BongloyAPIClient

#import "BongloyAPIClient.h"

Create a Token

let cardParams = STPCardParams()
cardParams.number = "6200000000000005"
cardParams.expMonth = 12
cardParams.expYear = 2020
cardParams.cvc = "123"

BongloyAPIClient.shared().createTokenWithCard(cardParams) { (token: STPToken?, error: Error?) in
  guard let token = token, error == nil else {
    // Present error to user...
    return
  }

  submitTokenToBackend(token, completion: { (error: Error?) in
    if let error = error {
      // Present error to user...
    }
    else {
      // Continue with payment...
    }
  })
}

Creating Charges

Once you’ve securely collected and tokenized your customer's credit card you can charge the card immediately or save it for later. Unlike tokenization, which occurs in the browser, charge attempts are made from your server, normally using one of our client libraries. If you haven't already, install the library for your favorite language now.

On your server, grab the Bongloy token in the POST parameters submitted by your form. From there, it’s one simple API call to charge the card:

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=999 \
  -d currency=usd \
  -d source=token_id \
  -d "metadata[order_id]=6735" \
  -d "description=Example Charge"

That's it! If the charge creation request succeeds, the card has been successfully charged. If the charge attempt fails, we'll return an error instead.

Note that a token can only be used once, and within a few minutes of creation. To support multiple charges, or future charges save the card token instead of charging it.

Dynamic statement descriptor

You can set the statement descriptor dynamically on every charge request with the statement_descriptor argument on the Charge object:

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=999 \
  -d currency=usd \
  -d source=token_id \
  -d "description=Example Charge" \
  -d "statement_descriptor=Custom descriptor"

Statement descriptors are limited to 22 characters, cannot use the special characters ><'" and must not consist solely of numbers.

Separate authorization and capture

Bongloy supports two-step card payments so you can first authorize a charge, then wait to settle (capture) it later. When a charge is authorized, the funds are guaranteed by the card issuer and the amount held on the customer's card for up to seven days. If the charge is not captured within this time, the authorization is canceled and funds released.

To authorize a payment without capturing it, make a charge request that also includes the capture parameter with a value of false. This instructs Bongloy to only authorize the amount on the customer's card.

If you need to cancel an authorization, you can release it by refunding the relevant Charge object.

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=999 \
  -d currency=usd \
  -d source=token_id \
  -d "description=Example Charge" \
  -d capture=false

To settle an authorized charge, make a capture charge request. The total authorized amount is captured by default, and you cannot capture more than this. To capture less than the initial amount (e.g., $8 of a $10 authorization), pass the amount parameter. Partially capturing a charge automatically releases the remaining amount.

$ curl -X POST https://api.bongloy.com/v1/charges/f1cb560f-e0ac-454e-9bf1-2917bff4d978/capture \
  -u sk_test_abcde...:

Authorized charges can only be captured once. If you partially capture a charge, you cannot perform another capture for the difference. Depending on your requirements, you may be better served by saving customer's card details for later and creating charges as needed.

Storing information as metadata

Bongloy supports adding metadata to the most common requests you make, such as processing charges. Metadata isn't shown to customers or displayed on their statements.

Through metadata, you can associate other information, which is meaningful to you, with Bongloy activity. Any metadata you include is viewable in the Dashboard (e.g., when looking at the page for an individual charge), and is also available in common reports and exports. As an example, your store's order ID can be attached to the charge used to pay for that order. Doing so allows you, your accountant, or your finance team to easily reconcile charges in Bongloy to orders in your system.

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=999 \
  -d currency=usd \
  -d source=token_id \
  -d "description=Example Charge" \
  -d "metadata[order_id]=6735"

Do not store any sensitive information (personally identifiable information, card details, etc.) as metadata or in the charge's description parameter.

Saving Cards

When you collect a customer's payment information, a Bongloy token is created. This token can only be used once, but that doesn't mean you have to request your customer's card details for every payment.

Bongloy provides a Customer object that makes it easy to save you customer's payment information for later use. You can use Customer objects for creating subscriptions or future one-off charges.

Saving credit card details for later

To make a card available for later charging, create a new Customer instead of a Charge by providing their tokenized card information and optionally an email address. Be certain to store the customer ID on your side for later use. You can subsequently charge that customer by passing the customer ID, instead of a card representation in the charge request.

# Create a customer
$ curl https://api.bongloy.com/v1/customers \
  -u sk_test_abcde...: \
  -d source=token_id \
  -d "email=paying.user@example.com"

# YOUR CODE: Save the customer ID and other info in a database for later.

# Charge the customer instead of the card
$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=1000 \
  -d currency=usd \
  -d customer=customer_id

# When it's time to charge the customer again, use the customer ID.
$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=1500 \
  -d currency=usd \
  -d customer=customer_id

Bongloy Connect

Marketplaces and platforms use Bongloy Connect to accept money and pay out to third parties. Connect enables you to use the Bongloy API on behalf of connected accounts.

Use cases

Connect is a flexible combination of features designed to support a wide range of use cases:

  • Crowdfunding services
  • E-commerce platforms, like Shopify
  • Marketplaces
  • On-demand services
  • Invoicing solutions, like QuickBooks
  • Booking platforms
  • Travel and event providers
  • Extensions on Bongloy

The OAuth connection flow

A user connects to your platform using the following OAuth connection flow:

  1. Starting on a page at your site, the user clicks a link that takes them to Bongloy, passing along your platform's client_id.
  2. On Bongloy's website, the user provides the necessary information for connecting to your platform.
  3. The user is then redirected back to your site along with an authorization code.
  4. Your site then makes a request to Bongloy's OAuth token endpoint to complete the connection and fetch the user's account ID.

When these steps are done, API requests can be made on behalf of the user with their account ID.

Step 2: User creates or connects their account

After the user clicks the link on your site, they'll be taken to Bongloy's website where they'll be prompted to allow or deny the connection to your platform.

Step 3: User is redirected back to your site

After the user connects their existing or newly created account to your platform, they are redirected back to your site, to the URL established as your platform's redirect_uri.

For successful connections, we'll pass along in the URL:

  • The scope granted
  • The state value, if provided
  • An authorization code

https://www.example.com/auth/bongloy/callback?code=AUTHORIZATION_CODE

If the authorization was denied by the user, they'll still be redirected back to your site, but the URL includes an error instead of the authorization code:

https://www.example.com/auth/bongloy/callback?error=access_denied&error_description=The%20user%20denied%20your%20request

Step 4: Fetch the user's credentials from Bongloy

Assuming no error occurred, the last step is to use the provided code to make a POST request to our access_token_url endpoint to fetch the user's Bongloy credentials. There are two endpoints, one for the sandbox and one for production:

Sandbox endpoint
https://sandbox.bongloy.com/oauth/token
Production endpoint
https://www.bongloy.com/oauth/token

Below is an example request to our sandbox endpoint:

$ curl https://sandbox.bongloy.com/oauth/token \
  -d "client_id=CLIENT_ID" \
  -d "client_secret=CLIENT_SECRET" \
  -d "code=AUTHORIZATION_CODE" \
  -d "grant_type=authorization_code" \
  -d "redirect_uri=https://www.example.com/auth/bongloy/callback"

Bongloy returns a response containing the authentication credentials for the user:

{
  "access_token": "ACCESS_TOKEN",
  "bongloy_account_id": "ACCOUNT_ID",
  "publishable_key": "PUBLISHABLE_KEY",
  "token_type": "Bearer"
}

If there was a problem, we instead return an error:

{
  "error": "invalid_grant",
  "error_description": "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
}

You’re done! The user is now connected to your platform. Store the bongloy_account_id in your database; this is the Bongloy account ID for the new account. You'll use this value to authenticate as the connected account by passing it into requests in the Bongloy-Account header.

In your application, you might want to consider using a dedicated OAuth client library to simplify these steps. To find an OAuth library for your language or framework, you can refer to the list of client libraries on the OAuth website.

Revoked access

An account.application.deauthorized event occurs when a user disconnects your platform from their account. By watching for this event via webhooks you can perform any necessary cleanup on your servers.

Authentication with Connect

In order to authenticate as a user under your platform use the Bongloy-Account header. This requires that you store the connected account's ID when connecting the account to your platform.

This curl request performs a refund of a charge on behalf of a connected account:

$ curl https://api.bongloy.com/v1/refunds \
  -u sk_test_abcde...: \
  -d charge=charge_id \
  -H "Bongloy-Account: CONNECTED_BONGLOY_ACCOUNT_ID"

Creating Charges

With Bongloy Connect, you can make charges directly on the connected account and take fees in the process.

When creating charges on the connected account, the connected account is responsible for the cost of the Bongloy fees, refunds, and chargebacks.

To create a charge the connected account, perform a standard create charge request using your platform's secret key while authenticated as the connected account:

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=1000 \
  -d currency=usd \
  -d source=token_id \
  -H "Bongloy-Account: CONNECTED_BONGLOY_ACCOUNT_ID"

When creating charges on the connected account, you must provide a token created under the connected account. You create a token under the connected account by passing the Bongloy-Account header. This is done automatically for you when using Bongloy.js. Just pass the the bongloyAccount option when setting your publishable key.

Charges created directly on the connected account are only reported on that account; they aren't shown in your platform's Dashboard, exports, or other reporting, although you can always retrieve this information via the API.

Collecting application fees

With Connect, your platform can take an application fee on charges. To apply an application fee on a charge, pass an optional application_fee_amount value as a positive integer:

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=1000 \
  -d currency=usd \
  -d source=token_id \
  -d application_fee_amount=123 \
  -H "Bongloy-Account: CONNECTED_BONGLOY_ACCOUNT_ID"

There are three things to note about application fees:

  • While you may pass in any positive number, the final amount collected as the application fee is capped at the total transaction amount minus any Bongloy fees.
  • There are no additional Bongloy fees on the application fee itself

The resulting charge's balance transaction includes a detailed fee breakdown of both the Bongloy and application fees. To provide a better reporting experience, an application fee object is created once the fee is collected. Use the amount property on the application fee object for reporting. You can then access these objects with the Application Fees endpoint.

Earned application fees are added to your available account balance on the same schedule as funds from regular Bongloy charges. Application fees are viewable in the Collected Fees section of the Dashboard.

Flow of funds with fees

When you specify an application fee on a charge, the fee amount is transferred to your platform's Bongloy account. When processing a charge directly on the connected account, the charge amount, less the Bongloy fees and application fee, is deposited into the connected account. For example, if a charge of $10 with a $1.23 application fee is made (as in the example above), $1.23 is transferred to your platform account, and $8.23 ($10 - $0.54 - $1.23) is netted in the connected account (assuming standard Bongloy fees.

Issuing refunds

Just as platforms can create charges on connected accounts, they can also create refunds of charges on connected accounts. To refund a charge on a connected account, perform a standard create refund request using your platform's secret key while authenticated as the connected account.

Shared customers

For some business models, it's helpful to reuse your customers' payment information across connected accounts. For example, a customer who makes a purchase from one of your connected sellers shouldn't need to re-enter their credit card to purchase from another seller.

With Connect, you can accomplish this by following three steps:

  1. Storing customers with a payment method, on the platform account
  2. Making tokens when it's time to charge the customer on behalf of a connected account
  3. Creating charges Creating charges using the new tokens

Step 1: Storing customers

To share customers, you need save them on the platform Bongloy account. This is a simple API call using the platform accounts secret key.

$ curl https://api.bongloy.com/v1/customers \
  -u sk_test_abcde...: \
  -d email="paying.user@example.com" \
  -d source=token_id

Step 2: Making tokens

When you're ready to create a charge on a connected account using a customer saved on your platform account, create a new token for that purpose. You’ll need:

  • The Bongloy account ID of the connected account for whom you're making the charge.
  • The customer ID of the customer (in your platform account) being charged.
  • The card for that customer, if you want to charge a specific card rather than the default.

$ curl https://api.bongloy.com/v1/tokens \
  -u sk_test_abcde...: \
  -d customer="customer_id" \
  -H "Bongloy-Account: CONNECTED_BONGLOY_ACCOUNT_ID"

Step 3: Creating charges

Like a regular token created by Bongloy.js, the one generated in the previous step acts as a credit card number. You can use the token to create a one-time payment as shown below.

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=1000 \
  -d currency=usd \
  -d source=token_id \
  -H "Bongloy-Account: CONNECTED_BONGLOY_ACCOUNT_ID"

Bongloy API Reference

Wherever possible, Bongloy's API is compatible with Stripe's REST API. The examples in the API reference are cURL commands. While cURL is useful for testing, we highly recommend that you make use of Stripe's API libraries when building your application.

The examples in this documentation actually work. You can perform the requests using your test-mode secret API key which is linked to your account. If you login to the Bongloy Sandbox, your API keys will be automatically pre-filled for you.

Authentication

The Bongloy API uses API keys to authenticate requests. You can view and manage your API keys in the Bongloy Dashboard.

Test mode secret keys have the prefix sk_test_ and live mode secret keys have the prefix sk_live_.

Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.

Authentication to the API is performed via HTTP Basic Auth Provide your API key as the basic auth username value. You do not need to provide a password.

All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.

Example Request

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...:
# The colon prevents curl from asking for a password.

Pagination

All top-level API resources have support for bulk fetches via "list" API methods. For instance, you can list charges, list customers, and list refunds. These list API methods share a common structure, taking at least these three parameters: limit, starting_after, and ending_before.

Bongloy utilizes cursor-based pagination via the starting_after and ending_before parameters. Both parameters take an existing object ID value (see below) and return objects in reverse chronological order. The ending_before parameter returns objects listed before the named object. The starting_after parameter returns objects listed after the named object. If both parameters are provided, only ending_before is used.

Arguments

limit optional, default is 10
A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.

starting_after optional
A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.

ending_before optional
A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.
List Response Format

object string, value is "list"
A string describing the object type returned.

data array
An array containing the actual response elements, paginated by any request parameters.

has_more boolean
Whether or not there are more elements available after this set. If false, this set comprises the end of the list.

url string
The URL for accessing this list.
Example Request

$ curl https://api.bongloy.com/v1/charges?limit=3 \
  -u sk_test_abcde...: \
  -G

Example Response

{
  "has_more": true,
  "object": "list",
  "url": "/v1/charges",
  "data": [
    {
      "id": "c43b2992-6fe0-485c-a11b-848edeb50270",
      "object": "charge",
      "created": 1562115476,
      "livemode": false,
      "amount": 10000,
      "amount_refunded": 0,
      "balance_transaction": null,
      "captured": false,
      "currency": "USD",
      "customer": null,
      "description": "Charge for 1 case of Singha at KTV",
      "dispute": null,
      "failure_code": null,
      "failure_message": null,
      "application": null,
      "paid": true,
      "refunded": false,
      "refunds": {
        "has_more": false,
        "object": "list",
        "url": "/api/v1/charges",
        "data": []
      },
      "source": {
        "id": "f59240fc-3871-4729-a5be-2ed1f61f0b67",
        "object": "card",
        "created": 1562115452,
        "livemode": false,
        "address_city": null,
        "address_country": null,
        "address_line1": null,
        "address_line1_check": "unchecked",
        "address_line2": null,
        "address_state": null,
        "address_zip": null,
        "address_zip_check": "unchecked",
        "brand": "UnionPay",
        "country": "KH",
        "customer": null,
        "cvc_check": "unchecked",
        "exp_month": 10,
        "exp_year": 2021,
        "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
        "last4": "0005",
        "name": null
      },
      "statement_descriptor": "Singha",
      "status": "succeeded",
      "metadata": {
        "foo": "bar"
      },
      "outcome": {
        "type": "authorized",
        "reason": null,
        "risk_level": "normal",
        "network_status": "approved_by_network"
      },
      "application_fee_amount": null,
      "application_fee": null
    },
    {...},
    {...}
  ]
}

Metadata

Some Bongloy objects including charges, customers, and refunds have a metadata parameter. You can use this parameter to attach key-value data to these Bongloy objects.

Metadata is useful for storing additional, structured information on an object. As an example, you could store your user's full name and corresponding unique identifier from your system on a Bongloy Customer object. Metadata is not used by Bongloy. For example, it's not used to authorize or decline a charge and won't be seen by your users unless you choose to show it to them.

Some of the objects listed above also support a description parameter. You can use the description parameter to annotate a charge with, for example, a human-readable description like 2 shirts for test@example.com. Unlike metadata, description is a single string.

Do not store any sensitive information (personally identifiable information, card details, etc.) as metadata or in the description parameter.

Example Request

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=10000 \
  -d currency=usd \
  -d source=token_id \
  -d "metadata[order_id]=6735" \
  -d "description=Charge for 1 case of Singha at KTV"

Example Response

{
  "id": "f1cb560f-e0ac-454e-9bf1-2917bff4d978",
  "metadata": {
    "order_id": "6735"
  },
  "description": "Charge for 1 case of Singha at KTV",
  ...
}

Application Fees

When you collect a transaction fee on top of a charge made for your user (using Connect), an Application Fee object is created in your account. You can list and retrieve application fees. For details, see Collecting application fees.

Retrieve an application fee

Retrieves the details of an application fee that your account has collected.

Arguments

id required
The identifier of the fee to be retrieved.
Returns

Returns the ApplicationFee object.

Example Request

curl https://api.bongloy.com/v1/application_fees/8bb212e9-6b57-4df1-8dff-7168f898849d \
  -u sk_test_abcde...:

Example Response

{
  "id": "8bb212e9-6b57-4df1-8dff-7168f898849d",
  "object": "application_fee",
  "account": "91f6012e-79ed-49a6-8d70-4f01a85cb25d",
  "amount": 50,
  "application": "472d7ef6-8c0a-4aab-a497-921ca258911f",
  "balance_transaction": "398ebfe9-2d8a-4296-85eb-8869ccc9e564",
  "charge": "ffee13a9-42b9-4682-96eb-43265521ff23",
  "currency": "USD",
  "created": 1562533549
}

List all application fees

Returns a list of application fees you've previously collected. The application fees are returned in sorted order, with the most recent fees appearing first.

Arguments

ending_before optional
A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.

limit optional
A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.

starting_after optional
A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.
Returns

A dictionary with a data property that contains an array of up to limit application fees, starting after application fee starting_after. Each entry in the array is a separate application fee object. If no more application fees are available, the resulting array will be empty.

Example Request

curl https://api.bongloy.com/v1/application_fees?limit=3 \
  -u sk_test_abcde...:

Example Response

{
  "has_more": false,
  "object": "list",
  "url": "/v1/application_fees",
  "data": [
    {
      "id": "38c76d87-bab1-4ff7-a8ec-1ae06af89c86",
      "object": "application_fee",
      "account": "5a21e708-5cd0-4d46-8e78-18bd21be0804",
      "amount": 50,
      "application": "a6a04f4a-47cf-48b4-980b-e6900d925010",
      "balance_transaction": "00627b3b-d236-4b6d-8d30-54326cb9ef51",
      "charge": "caa818f1-3d77-4b7b-85b8-21097e83a56a",
      "currency": "USD",
      "created": 1562533837
    }
  ]
}

Balance

This is an object representing your Bongloy balance. You can retrieve it to see the balance currently on your Bongloy account.

You can also retrieve the balance history, which contains a list of transactions that contributed to the balance (charges, payouts, and so forth).

Retrieves balance

Retrieves the current account balance.

Arguments

No arguments


Returns

Returns a Balance object for the account that was authenticated in the request.

Example Request

curl https://api.bongloy.com/v1/balance \
  -u sk_test_abcde...:

Example Response

{
  "object": "balance",
  "livemode": false,
  "available": [
    {
      "amount": 10000,
      "currency": "USD",
      "source_types": {
        "card": 10000
      }
    }
  ],
  "pending": [
    {
      "amount": 5000,
      "currency": "USD",
      "source_types": {
        "card": 5000
      }
    }
  ]
}

Retrieve a balance transaction

Retrieves the balance transaction with the given ID.

Arguments

id required
The ID of the desired balance transaction, as found on any API object that affects the balance (e.g., a charge or transfer).
Returns

Returns the BalanceTransaction object.

Example Request

curl https://api.bongloy.com/v1/balance/history/f488a05a-4359-409b-9472-f3b07b1d8e12 \
  -u sk_test_abcde...:

Example Response

{
  "id": "f488a05a-4359-409b-9472-f3b07b1d8e12",
  "object": "balance_transaction",
  "amount": 10000,
  "available_on": 1558915200,
  "created": 1558343366,
  "currency": "USD",
  "fee": 1200,
  "fee_details": [
    {
        "amount": 200,
        "currency": "USD",
        "type": "tax"
    },
    {
      "amount": 1000,
      "currency": "USD",
      "type": "bongloy_fee"
    }
  ],
  "net": 8800,
  "status": "available",
  "source": "dd08b150-ac0f-477a-ae56-14695079edc7",
  "type": "charge"
}

List all balance history

Returns a list of transactions that have contributed to the Bongloy account balance (e.g., charges, payouts, and so forth). The transactions are returned in sorted order, with the most recent transactions appearing first.

Arguments

ending_before optional
A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.

limit optional
A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.

starting_after optional
A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.
Returns

A dictionary with a data property that contains an array of up to limit transactions, starting after transaction starting_after. Each entry in the array is a separate transaction object. If no more transactions are available, the resulting array will be empty.

Example Request

curl https://api.bongloy.com/v1/balance/history?limit=3 \
  -u sk_test_abcde...:

Example Response

{
  "has_more": true,
  "object": "list",
  "url": "/v1/balance/history",
  "data": [
    {
      "id": "f488a05a-4359-409b-9472-f3b07b1d8e12",
      "object": "balance_transaction",
      "amount": 10000,
      "available_on": 1558915200,
      "created": 1558343366,
      "currency": "USD",
      "fee": 2000,
      "fee_details": [
        {
          "amount": 800,
          "currency": "USD",
          "type": "tax"
        },
        {
          "amount": 1200,
          "currency": "USD",
          "type": "bongloy_fee"
        }
      ],
      "net": 8000,
      "status": "available",
      "source": "dd08b150-ac0f-477a-ae56-14695079edc7",
      "type": "charge"
    },
    {...},
    {...}
  ]
}

Charges

To charge a credit card, you create a new Charge object. You can retrieve and refund individual charges as well as list all charges. Charges are identified by a unique, random ID.

Create a new charge

To charge a credit card, you create a new Charge object. If your API key is in test mode, the supplied card won't actually be charged, though everything else will occur as if in live mode. (Bongloy assumes that the charge would have completed successfully).

Arguments

amount required
A positive integer representing how much to charge, in the smallest currency unit (e.g., 100 cents to charge $1.00). The minimum amount is $1.00 USD.

currency required
Three-letter ISO currency code in lowercase. Only "usd" is supported at this time.

application_fee_amount optional
A fee in cents that will be applied to the charge and transferred to the application owner's Bongloy account. The request must be made with the Bongloy-Account header in order to take an application fee. For more information, see the application fees documentation.

capture optional
Whether to immediately capture the charge. Defaults to true. When false, the charge issues a pre-authorization, and will need to be captured later. Uncaptured charges expire in seven days. For more information, see the authorizing charges and settling later documentation.

customer optional
The ID of an existing customer that will be charged in this request.

description optional
An arbitrary string which you can attach to a Charge> object. It is displayed when in the web interface alongside the charge. This will be unset if you POST an empty value.

metadata optional
Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format.

source optional
A payment source to be charged. This can be the ID of a token (i.e. created by Bongloy.js ) or a card (i.e., credit or debit card). If the source is a card, you must also pass the ID of the associated customer.

statement_descriptor optional
An arbitrary string to be displayed on your customer's credit card statement. This can be up to 22 characters. As an example, if your website is RunClub and the item you're charging for is a race ticket, you might want to specify a statement_descriptor of RunClub 5K race ticket. The statement descriptor must contain at least one letter, must not contain ><'" characters, and will appear on your customer’s statement in capital letters. Non-ASCII characters are automatically stripped. While most banks and card issuers display this information consistently, some might display it incorrectly or not at all.
Returns

Returns a Charge object if the charge succeeded. Returns an error if something goes wrong. A common source of error is an invalid or expired card, or a valid card with insufficient available balance.

Example Request: Charging a Card

To charge a card you first need to create a token that represents the card. You can create a token using Bongloy.js

$ curl https://api.bongloy.com/v1/charges \
  -u sk_test_abcde...: \
  -d amount=10000 \
  -d currency=usd \
  -d source=token_id \
  -d "description=Charge for 1 case of Singha at KTV" \
  -d "statement_descriptor=Singha" \
  -d "metadata[foo]=bar"

Example Response

{
  "id": "68868dd7-5b75-4f19-b11a-c74900a73980",
  "object": "charge",
  "created": 1562114274,
  "livemode": false,
  "amount": 10000,
  "amount_refunded": 0,
  "balance_transaction": "ceb8731b-6986-4cdd-a8e7-49b55e9f125c",
  "captured": true,
  "currency": "USD",
  "customer": null,
  "description": "Charge for 1 case of Singha at KTV",
  "dispute": null,
  "failure_code": null,
  "failure_message": null,
  "application": null,
  "paid": true,
  "refunded": false,
  "refunds": {
    "has_more": false,
    "object": "list",
    "url": "/v1/charges/68868dd7-5b75-4f19-b11a-c74900a73980/refunds",
    "data": []
  },
  "source": {
    "id": "cb0a65ff-9a03-4750-9c83-3ca112876875",
    "object": "card",
    "created": 1562114231,
    "livemode": false,
    "address_city": null,
    "address_country": null,
    "address_line1": null,
    "address_line1_check": "unchecked",
    "address_line2": null,
    "address_state": null,
    "address_zip": null,
    "address_zip_check": "unchecked",
    "brand": "UnionPay",
    "country": "KH",
    "customer": null,
    "cvc_check": "unchecked",
    "exp_month": 10,
    "exp_year": 2021,
    "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
    "last4": "0005",
    "name": null
  },
  "statement_descriptor": "Singha",
  "status": "succeeded",
  "metadata": {
    "foo": "bar"
  },
  "outcome": {
    "network_status": "approved_by_network",
    "risk_level": "normal",
    "type": "authorized",
    "reason": null
  },
  "application_fee_amount": null,
  "application_fee": null
}

Retrieve an existing charge

Retrieves the details of a charge that has previously been created. Supply the unique charge ID that was returned from your previous request, and Bongloy will return the corresponding charge information. The same information is returned when creating the charge.

Arguments

charge required
The identifier of the charge to be retrieved.
Returns

Returns the Charge object.

Example Request

$ curl https://api.bongloy.com/v1/charges/f1cb560f-e0ac-454e-9bf1-2917bff4d978 \
-u sk_test_abcde...:

Example Response

{
  "id": "68868dd7-5b75-4f19-b11a-c74900a73980",
  "object": "charge",
  "created": 1562114274,
  "livemode": false,
  "amount": 10000,
  "amount_refunded": 0,
  "balance_transaction": "ceb8731b-6986-4cdd-a8e7-49b55e9f125c",
  "captured": true,
  "currency": "USD",
  "customer": null,
  "description": "Charge for 1 case of Singha at KTV",
  "dispute": null,
  "failure_code": null,
  "failure_message": null,
  "application": null,
  "paid": true,
  "refunded": false,
  "refunds": {
    "has_more": false,
    "object": "list",
    "url": "/v1/charges/68868dd7-5b75-4f19-b11a-c74900a73980/refunds",
    "data": []
  },
  "source": {
    "id": "cb0a65ff-9a03-4750-9c83-3ca112876875",
    "object": "card",
    "created": 1562114231,
    "livemode": false,
    "address_city": null,
    "address_country": null,
    "address_line1": null,
    "address_line1_check": "unchecked",
    "address_line2": null,
    "address_state": null,
    "address_zip": null,
    "address_zip_check": "unchecked",
    "brand": "UnionPay",
    "country": "KH",
    "customer": null,
    "cvc_check": "unchecked",
    "exp_month": 10,
    "exp_year": 2021,
    "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
    "last4": "0005",
    "name": null
  },
  "statement_descriptor": "Singha",
  "status": "succeeded",
  "metadata": {
    "foo": "bar"
  },
  "outcome": {
    "type": "authorized",
    "reason": null,
    "risk_level": "normal",
    "network_status": "approved_by_network"
  },
  "application_fee_amount": null,
  "application_fee": null
}

Capture a charge

Capture the payment of an existing, uncaptured, charge. This is the second half of the two-step payment flow, where first you created a charge with the capture option set to false.

Uncaptured payments expire exactly seven days after they are created. If they are not captured by that point in time, they will be marked as refunded and will no longer be capturable.

Arguments

charge required
The identifier of the charge to be captured.

amount optional
The amount to capture, which must be less than or equal to the original amount. Any additional amount will be automatically refunded.

application_fee_amount optional
An application fee amount to add on to this charge, which must be less than or equal to the original amount. Can only be used with Bongloy Connect.
Returns

Returns the charge object, with an updated captured property (set to true). Capturing a charge will always succeed, unless the charge is already refunded, expired, captured, or an invalid capture amount is specified, in which case this method will return an error.

Example Request

$ curl -X POST https://api.bongloy.com/v1/charges/f1cb560f-e0ac-454e-9bf1-2917bff4d978/capture \
  -u sk_test_abcde...:

Example Response

{
  "id": "56171c87-9d66-4e8e-b83c-b622a3b22e4f",
  "object": "charge",
  "created": 1562115953,
  "livemode": false,
  "amount": 10000,
  "amount_refunded": 0,
  "balance_transaction": "d4a645f0-4024-4611-bcc9-4696a81c78a1",
  "captured": true,
  "currency": "USD",
  "customer": null,
  "description": "Charge for 1 case of Singha at KTV",
  "dispute": null,
  "failure_code": null,
  "failure_message": null,
  "application": null,
  "paid": true,
  "refunded": false,
  "refunds": {
    "has_more": false,
    "object": "list",
    "url": "/v1/charges/56171c87-9d66-4e8e-b83c-b622a3b22e4f/refunds",
    "data": []
  },
  "source": {
    "id": "117e7e4e-a89e-400b-9fff-8105efc73ac2",
    "object": "card",
    "created": 1562115764,
    "livemode": false,
    "address_city": null,
    "address_country": null,
    "address_line1": null,
    "address_line1_check": "unchecked",
    "address_line2": null,
    "address_state": null,
    "address_zip": null,
    "address_zip_check": "unchecked",
    "brand": "UnionPay",
    "country": "KH",
    "customer": null,
    "cvc_check": "unchecked",
    "exp_month": 10,
    "exp_year": 2021,
    "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
    "last4": "0005",
    "name": null
  },
  "statement_descriptor": "Singha",
  "status": "succeeded",
  "metadata": {
    "foo": "bar"
  },
  "outcome": {
    "type": "authorized",
    "reason": null,
    "risk_level": "normal",
    "network_status": "approved_by_network"
  },
  "application_fee_amount": null,
  "application_fee": null
}

List all charges

Returns a list of charges you’ve previously created. The charges are returned in sorted order, with the most recent charges appearing first.

Arguments

ending_before optional
A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.

limit optional
A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.

starting_after optional
A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.
Returns

A dictionary with a data property that contains an array of up to limit charges, starting after charge starting_after. Each entry in the array is a separate charge object. If no more charges are available, the resulting array will be empty.

Example Request

$ curl https://api.bongloy.com/v1/charges?limit=3 \
  -u sk_test_abcde...: \
  -G

Example Response

{
  "has_more": true,
  "object": "list",
  "url": "/v1/charges",
  "data": [
    {
      "id": "c43b2992-6fe0-485c-a11b-848edeb50270",
      "object": "charge",
      "created": 1562115476,
      "livemode": false,
      "amount": 10000,
      "amount_refunded": 0,
      "balance_transaction": null,
      "captured": false,
      "currency": "USD",
      "customer": null,
      "description": "Charge for 1 case of Singha at KTV",
      "dispute": null,
      "failure_code": null,
      "failure_message": null,
      "application": null,
      "paid": true,
      "refunded": false,
      "refunds": {
        "has_more": false,
        "object": "list",
        "url": "/api/v1/charges",
        "data": []
      },
      "source": {
        "id": "f59240fc-3871-4729-a5be-2ed1f61f0b67",
        "object": "card",
        "created": 1562115452,
        "livemode": false,
        "address_city": null,
        "address_country": null,
        "address_line1": null,
        "address_line1_check": "unchecked",
        "address_line2": null,
        "address_state": null,
        "address_zip": null,
        "address_zip_check": "unchecked",
        "brand": "UnionPay",
        "country": "KH",
        "customer": null,
        "cvc_check": "unchecked",
        "exp_month": 10,
        "exp_year": 2021,
        "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
        "last4": "0005",
        "name": null
      },
      "statement_descriptor": "Singha",
      "status": "succeeded",
      "metadata": {
        "foo": "bar"
      },
      "outcome": {
        "type": "authorized",
        "reason": null,
        "risk_level": "normal",
        "network_status": "approved_by_network"
      },
      "application_fee_amount": null,
      "application_fee": null
    },
    {...},
    {...}
  ]
}

Customers

Customer objects allow you to perform recurring charges and track multiple charges that are associated with the same customer. The API allows you to create, delete, and update your customers. You can retrieve individual customers as well as a list of all your customers.

Create a new customer

Creates a customer object

Arguments

description optional
An arbitrary string that you can attach to a customer object. It is displayed alongside the customer in the dashboard. This will be unset if you POST an empty value.

email optional
Customer's email address. It's displayed alongside the customer in your dashboard and can be useful for searching and tracking.

metadata optional
Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format.

source optional
The source is the ID of a Token, as returned by Bongloy.js. Passing source will create a new card object and make it the customer default source. If you want to add an additional source to an existing customer instead, use the card creation API.
Returns

Returns a Customer object if the call succeeded. If a source has been attached to the customer, the returned customer object will have a default_source attribute, which is an ID that can be expanded into the full source details when retrieving the customer.

Example Request

$ curl https://api.bongloy.com/v1/customers \
  -u sk_test_abcde...: \
  -d source=token_id \
  -d "description=Customer for jenny.rosen@example.com" \
  -d "metadata[foo]=bar"

Example Response

{
  "id": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
  "object": "customer",
  "created": 1553324006,
  "livemode": false,
  "default_source": "b817ff09-bd58-4dca-8349-fed5e1d59a67",
  "description": "Customer for jenny.rosen@example.com",
  "email": null,
  "sources": {
    "has_more": false,
    "object": "list",
    "url": "/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41/sources",
    "data": [
      {
        "id": "b817ff09-bd58-4dca-8349-fed5e1d59a67",
        "object": "card",
        "created": 1553323914,
        "livemode": false,
        "address_city": null,
        "address_country": null,
        "address_line1": null,
        "address_line1_check": "unchecked",
        "address_line2": null,
        "address_state": null,
        "address_zip": null,
        "address_zip_check": "unchecked",
        "brand": "UnionPay",
        "country": "KH",
        "customer": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
        "cvc_check": "unchecked",
        "exp_month": 12,
        "exp_year": 2019,
        "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
        "last4": "0005",
        "name": null
      }
    ]
  },
  "metadata": {
    "foo": "bar"
  }
}

Retrieve an existing customer

Retrieves the details of an existing customer. You need only supply the unique customer identifier that was returned upon customer creation.

Request Parameters

id required
The identifier of the customer to be retrieved.
Returns

Returns the Customer object.

Example Request

$ curl https://api.bongloy.com/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41 \
  -u sk_test_abcde...:

Example Response

{
  "id": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
  "object": "customer",
  "created": 1553324006,
  "livemode": false,
  "default_source": "b817ff09-bd58-4dca-8349-fed5e1d59a67",
  "description": "Customer for jenny.rosen@example.com",
  "email": null,
  "sources": {
    "has_more": false,
    "object": "list",
    "url": "/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41/sources",
    "data": [
      {
        "id": "b817ff09-bd58-4dca-8349-fed5e1d59a67",
        "object": "card",
        "created": 1553323914,
        "livemode": false,
        "address_city": null,
        "address_country": null,
        "address_line1": null,
        "address_line1_check": "unchecked",
        "address_line2": null,
        "address_state": null,
        "address_zip": null,
        "address_zip_check": "unchecked",
        "brand": "UnionPay",
        "country": "KH",
        "customer": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
        "cvc_check": "unchecked",
        "exp_month": 12,
        "exp_year": 2019,
        "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
        "last4": "0005",
        "name": null
      }
    ]
  },
  "metadata": {
    "foo": "bar"
  }
}

List all customers

Returns a list of your customers. The customers are returned sorted by creation date, with the most recent customers appearing first.

Arguments

ending_before optional
A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.

limit optional
A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.

starting_after optional
A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.
Returns

A dictionary with a data property that contains an array of up to limit customers, starting after customer starting_after. Each entry in the array is a separate customer object. If no more customers are available, the resulting array will be empty.

Example Request

$ curl https://api.bongloy.com/v1/customers?limit=3 \
  -u sk_test_abcde...: \
  -G

Example Response

{
  "has_more": false,
  "object": "list",
  "url": "/v1/customers",
  "data": [
    {
      "id": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
      "object": "customer",
      "created": 1553324006,
      "livemode": false,
      "default_source": "b817ff09-bd58-4dca-8349-fed5e1d59a67",
      "description": "Customer for jenny.rosen@example.com",
      "email": null,
      "sources": {
        "has_more": false,
        "object": "list",
        "url": "/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41/sources",
        "data": [
          {
            "id": "b817ff09-bd58-4dca-8349-fed5e1d59a67",
            "object": "card",
            "created": 1553323914,
            "livemode": false,
            "address_city": null,
            "address_country": null,
            "address_line1": null,
            "address_line1_check": "unchecked",
            "address_line2": null,
            "address_state": null,
            "address_zip": null,
            "address_zip_check": "unchecked",
            "brand": "UnionPay",
            "country": "KH",
            "customer": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
            "cvc_check": "unchecked",
            "exp_month": 12,
            "exp_year": 2019,
            "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
            "last4": "0005",
            "name": null
          }
        ]
      },
      "metadata": {
        "foo": "bar"
      }
    },
    {...},
    {...}
  ]
}

Cards

You can store multiple cards on a customer in order to charge the customer later.

Create a new card

When you create a new card, you must specify a customer to create it on. If the card's owner has no default card, then the new card will become the default. However, if the owner already has a default then it will not change.

Arguments

source required
The ID of a Token, as returned by Bongloy.js.
Returns

Returns a Card object.

Example Request

To create a card you first need to create a customer and a token that represents the customer's card. You can create a customer by using the Customers API. For the token, you can create one using Bongloy.js

$ curl https://api.bongloy.com/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41/sources \
  -u sk_test_abcde...: \
  -d source=token_id

Example Response

{
  "id": "b783dfe1-5b76-4005-8adf-a53dd5faa9da",
  "object": "card",
  "created": 1553324655,
  "livemode": false,
  "address_city": null,
  "address_country": null,
  "address_line1": null,
  "address_line1_check": "unchecked",
  "address_line2": null,
  "address_state": null,
  "address_zip": null,
  "address_zip_check": "unchecked",
  "brand": "UnionPay",
  "country": "KH",
  "customer": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
  "cvc_check": "unchecked",
  "exp_month": 12,
  "exp_year": 2019,
  "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
  "last4": "0007",
  "name": null
}

Retrieve an existing card

You can always see the 10 most recent cards directly on a customer; this method lets you retrieve details about a specific card stored on the customer.

Arguments

id required
The ID of the card to be retrieved.
Returns

Returns the Card object.

Example Request

$ curl https://api.bongloy.com/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41/sources/b783dfe1-5b76-4005-8adf-a53dd5faa9da \
  -u sk_test_abcde...:

Example Response

{
  "id": "b783dfe1-5b76-4005-8adf-a53dd5faa9da",
  "object": "card",
  "created": 1553324655,
  "livemode": false,
  "address_city": null,
  "address_country": null,
  "address_line1": null,
  "address_line1_check": "unchecked",
  "address_line2": null,
  "address_state": null,
  "address_zip": null,
  "address_zip_check": "unchecked",
  "brand": "UnionPay",
  "country": "KH",
  "customer": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
  "cvc_check": "unchecked",
  "exp_month": 12,
  "exp_year": 2019,
  "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
  "last4": "0007",
  "name": null
}

Delete a card

You can delete cards from a customer.

If you delete a card that is currently the default source, then the most recently added source will become the new default. If you delete a card that is the last remaining source on the customer, then the default_source attribute will become null.

Arguments

id required
The ID of the source to be deleted.
Returns

204 No Content

Example Request

$ curl https://api.bongloy.com/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41/sources/b783dfe1-5b76-4005-8adf-a53dd5faa9da \
  -u sk_test_abcde...: \
  -X DELETE

Example Response
204 No Content

List all Cards

You can see a list of the cards belonging to a customer. Note that the 10 most recent sources are always available on the Customer object. If you need more than those 10, you can use this API method and the limit and starting_after parameters to page through additional cards.

Arguments

customer required
The ID of the customer whose cards will be retrieved.

ending_before optional
A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.

limit optional
A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.

starting_after optional
A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.
Returns

Returns a list of the cards stored on the customer.

Example Request

$ curl https://api.bongloy.com/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41/sources?limit=3 \
  -u sk_test_abcde...: \
  -G

Example Response

{
  "has_more": false,
  "object": "list",
  "url": "/v1/customers/826aacc5-a4c2-4b7a-807a-50c09ed64a41/sources",
  "data": [
    {
      "id": "b783dfe1-5b76-4005-8adf-a53dd5faa9da",
      "object": "card",
      "created": 1553324655,
      "livemode": false,
      "address_city": null,
      "address_country": null,
      "address_line1": null,
      "address_line1_check": "unchecked",
      "address_line2": null,
      "address_state": null,
      "address_zip": null,
      "address_zip_check": "unchecked",
      "brand": "UnionPay",
      "country": "KH",
      "customer": "826aacc5-a4c2-4b7a-807a-50c09ed64a41",
      "cvc_check": "unchecked",
      "exp_month": 12,
      "exp_year": 2019,
      "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
      "last4": "0007",
      "name": null
    },
    {...},
    {...}
  ]
}

Events

Events are our way of letting you know when something interesting happens in your account. When an interesting event occurs, we create a new Event object.

Retrieve an event

Retrieves the details of an event. Supply the unique identifier of the event, which you might have received in a webhook.

Arguments

id required
The identifier of the event to be retrieved
Returns

Returns an Event object if a valid identifier was provided. All events share a common structure, detailed to the right. The only property that will differ is the data property.

Example Request

$ curl https://api.bongloy.com/v1/events/dd37b8f1-cb74-4631-8352-e7eea3513483 \
  -u sk_test_abcde...:

Example Response

{
  "id": "dd37b8f1-cb74-4631-8352-e7eea3513483",
  "object": "event",
  "created": 1561002988,
  "livemode": false,
  "data": {
    "object": {
      "id": "b032a116-f561-44e9-98a1-73cfb5e1f5cb",
      "paid": true,
      "amount": 2000,
      "object": "charge",
      "source": {
        "id": "c91a9c0e-fc66-4260-b2ee-cebb477425ae",
        "name": null,
        "brand": "UnionPay",
        "last4": "0005",
        "object": "card",
        "country": "KH",
        "created": 1561002963,
        "customer": null,
        "exp_year": 2021,
        "livemode": false,
        "cvc_check": "unchecked",
        "exp_month": 1,
        "address_zip": null,
        "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
        "address_city": null,
        "address_line1": null,
        "address_line2": null,
        "address_state": null,
        "address_country": null,
        "address_zip_check": "unchecked",
        "address_line1_check": "unchecked"
      },
      "status": "succeeded",
      "created": 1561002988,
      "dispute": null,
      "outcome": {
        "type": "authorized",
        "reason": null,
        "risk_level": "normal",
        "network_status": "approved_by_network"
      },
      "refunds": {
        "url": "/v1/charges/b032a116-f561-44e9-98a1-73cfb5e1f5cb/refunds",
        "data": [],
        "object": "list",
        "has_more": false
      },
      "captured": true,
      "currency": "USD",
      "customer": null,
      "livemode": false,
      "metadata": {},
      "refunded": false,
      "application": null,
      "description": "\"Charge for jenny.rosen@example.com\"",
      "failure_code": null,
      "amount_refunded": 0,
      "application_fee": null,
      "failure_message": null,
      "balance_transaction": "1e2b130e-03a5-4635-ba34-fa4fe2f6008b",
      "statement_descriptor": null,
      "application_fee_amount": null
    }
  },
  "type": "charge.succeeded"
}

List all events

Returns a list of events

Arguments

ending_before optional
A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.

limit optional
A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.

starting_after optional
A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.
Returns

A dictionary with a data property that contains an array of up to limit refunds, starting after refund starting_after. Each entry in the array is a separate refund object. If no more refunds are available, the resulting array will be empty.

Example Request

$ curl https://api.bongloy.com/v1/events?limit=3 \
  -u sk_test_abcde...: \
  -G

Example Response

{
  "has_more": true,
  "object": "list",
  "url": "/v1/events",
  "data": [
    {
      "id": "dd37b8f1-cb74-4631-8352-e7eea3513483",
      "object": "event",
      "created": 1561002988,
      "livemode": false,
      "data": {
        "object": {
          "id": "b032a116-f561-44e9-98a1-73cfb5e1f5cb",
          "paid": true,
          "amount": 2000,
          "object": "charge",
          "source": {
            "id": "c91a9c0e-fc66-4260-b2ee-cebb477425ae",
            "name": null,
            "brand": "UnionPay",
            "last4": "0005",
            "object": "card",
            "country": "KH",
            "created": 1561002963,
            "customer": null,
            "exp_year": 2021,
            "livemode": false,
            "cvc_check": "unchecked",
            "exp_month": 1,
            "address_zip": null,
            "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
            "address_city": null,
            "address_line1": null,
            "address_line2": null,
            "address_state": null,
            "address_country": null,
            "address_zip_check": "unchecked",
            "address_line1_check": "unchecked"
          },
          "status": "succeeded",
          "created": 1561002988,
          "dispute": null,
          "outcome": {
            "type": "authorized",
            "reason": null,
            "risk_level": "normal",
            "network_status": "approved_by_network"
          },
          "refunds": {
            "url": "/v1/charges/b032a116-f561-44e9-98a1-73cfb5e1f5cb/refunds",
            "data": [],
            "object": "list",
            "has_more": false
          },
          "captured": true,
          "currency": "USD",
          "customer": null,
          "livemode": false,
          "metadata": {},
          "refunded": false,
          "application": null,
          "description": "\"Charge for jenny.rosen@example.com\"",
          "failure_code": null,
          "amount_refunded": 0,
          "application_fee": null,
          "failure_message": null,
          "balance_transaction": "1e2b130e-03a5-4635-ba34-fa4fe2f6008b",
          "statement_descriptor": null,
          "application_fee_amount": null
        }
      },
      "type": "charge.succeeded"
    },
    {...},
    {...}
  ]
}

Types of Events

This is a list of all the types of events we currently send We may add more at any time, so in developing and maintaining your code, you should not assume that only these types exist.

You'll notice that these events follow a pattern: resource.event. Our goal is to design a consistent system that makes things easier to anticipate and code against.


account.application.authorized describes an application
Occurs whenever a user authorizes an application. Sent to the related application only.

account.application.deauthorized describes an application
Occurs whenever a user deauthorizes an application. Sent to the related application only.

account.updated describes an account
Occurs whenever an account status or property has changed.

charge.captured describes a charge
Occurs whenever a previously uncaptured charge is captured.

charge.expired describes a charge
Occurs whenever an uncaptured charge expires.

charge.failed describes a charge
Occurs whenever a failed charge attempt occurs.

charge.refund.updated describes a refund
Occurs whenever a refund is updated, on selected payment methods.

charge.refunded describes a charge
Occurs whenever a charge is refunded, including partial refunds.

charge.succeeded describes a charge
Occurs whenever a new charge is created and is successful.

Refunds

Refund objects allow you to refund a charge that has previously been created but not yet refunded. Funds will be refunded to the credit or debit card that was originally charged.

Create a refund

When you create a new refund, you must specify a charge to create it on.

Creating a new refund will refund a charge that has previously been created but not yet refunded. Funds will be refunded to the credit or debit card that was originally charged. The fees you were originally charged are also refunded.

You can optionally refund only part of a charge. You can do so as many times as you wish until the entire charge has been refunded.

Once entirely refunded, a charge can't be refunded again. This method will return an error when called on an already-refunded charge, or when trying to refund more money than is left on a charge.

Arguments

charge required
The identifier of the charge to refund.

amount optional
Defaults to entire charge amount. A positive integer representing how much of this charge to refund. Can only refund up to the unrefunded amount remaining on the charge.

metadata optional
Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format.
Returns

Returns a Refund object if the refund succeeded. Returns an error if the charge has already been refunded or an invalid charge identifier was provided.

Example Request

To create a refund you first need to create a charge You can create a charge by using the Charges API.

curl -X POST https://api.bongloy.com/v1/refunds \
  -u sk_test_abcde...: \
  -d "charge=<charge_id>"

Example Response

{
  "id": "3ab28291-139e-4635-a65d-01d4f422436b",
  "object": "refund",
  "created": 1553326197,
  "amount": 1000,
  "balance_transaction": "6ce5cdad-fb62-4a96-a82f-2c814663afc8",
  "charge": "33be96dd-ce44-49b3-91b7-b21f9fcb33c2",
  "currency": "USD",
  "metadata": {},
  "status": "succeeded",
  "failure_balance_transaction": null
}

Retrieve a refund

Retrieves the details of an existing refund.

Arguments

refund_id required
The ID of the refund to retrieve.
Returns

Returns the Refund object.

Example Request

$ curl https://api.bongloy.com/v1/refunds/3ab28291-139e-4635-a65d-01d4f422436b \
  -u sk_test_abcde...:

Example Response

{
  "id": "3ab28291-139e-4635-a65d-01d4f422436b",
  "object": "refund",
  "created": 1553326197,
  "amount": 1000,
  "balance_transaction": "6ce5cdad-fb62-4a96-a82f-2c814663afc8",
  "charge": "33be96dd-ce44-49b3-91b7-b21f9fcb33c2",
  "currency": "USD",
  "metadata": {},
  "status": "succeeded",
  "failure_balance_transaction": null
}

List all refunds

Returns a list of all refunds you've previously created. The refunds are returned in sorted order, with the most recent refunds appearing first. For convenience, the 10 most recent refunds are always available by default on the charge object.

Arguments

ending_before optional
A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.

limit optional
A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.

starting_after optional
A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.
Returns

A dictionary with a data property that contains an array of up to limit refunds, starting after refund starting_after. Each entry in the array is a separate refund object. If no more refunds are available, the resulting array will be empty.

Example Request

$ curl https://api.bongloy.com/v1/refunds?limit=3 \
  -u sk_test_abcde...: \
  -G

Example Response

{
  "has_more": true,
  "object": "list",
  "url": "/v1/refunds",
  "data": [
    {
      "id": "3ab28291-139e-4635-a65d-01d4f422436b",
      "object": "refund",
      "created": 1553326197,
      "amount": 1000,
      "balance_transaction": "6ce5cdad-fb62-4a96-a82f-2c814663afc8",
      "charge": "33be96dd-ce44-49b3-91b7-b21f9fcb33c2",
      "currency": "USD",
      "metadata": {},
      "status": "succeeded",
      "failure_balance_transaction": null
    },
    {...},
    {...}
  ]
}

Tokens

Token objects allow you to create charges on a connected account using shared customers. This endpoint is only applicable for Bongloy Connect.

Create a card token

Creates a single-use token that represents a credit card's details. You can use this token to create a charge on a connected account.

Arguments

customer required
The customer (owned by the application's account) for which to create a token. This must be used with a Bongloy-Account header. For more details, see Shared customers.
Returns

Returns a Token object if successful.

Example Request

$ curl https://api.bongloy.com/v1/tokens \
  -u sk_test_abcde...: \
  -d customer=customer_id

Example Response

{
  "id": "3a3f1e44-2cab-40d5-b3f3-fc052d63f03f",
  "object": "token",
  "created": 1562808012,
  "livemode": false,
  "card": {
    "id": "00e5bf0d-f874-49e1-b91b-cf4bded64611",
    "object": "card",
    "created": 1562808012,
    "livemode": false,
    "address_city": null,
    "address_country": null,
    "address_line1": null,
    "address_line1_check": "unchecked",
    "address_line2": null,
    "address_state": null,
    "address_zip": null,
    "address_zip_check": "unchecked",
    "brand": "UnionPay",
    "country": "KH",
    "customer": null,
    "cvc_check": "unchecked",
    "exp_month": 7,
    "exp_year": 2019,
    "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
    "last4": "0005",
    "name": null
  },
  "client_ip": "127.0.0.1",
  "type": "card",
  "used": false
}

Test Cards

Number Description
6200000000000005 Debit card.
6200160000000007 Credit card.
62001400000009 Card with a 14 digit number.
620015000000008 Card with a 15 digit number.
62001700000000004 Card with a 17 digit number.
620018000000000005 Card with a 18 digit number.
6200190000000000000 Card with a 19 digit number.
6200160000000000 Valid card that fails luhn check.
6200010000000004 Invalid card.
6200160000000015 Charge is declined with an insufficient_funds code.
6200160000000023 Charge is declined with a card_not_supported code.
6200160000000031 Charge is declined with a card_expired code.
6200160000000049 Charge succeeds but refunding a captured charge fails with a card_expired code.

Webhooks

You can use webhooks to be notified about events that happen in your Bongloy account. This mechanism is especially useful for services that are not directly responsible for making an API request, but still need to know the response from that request. Webhooks are necessary only for behind the scenes transactions. Most Bongloy requests (e.g. creating charges or creating refunds) happen synchronously and don't require webhooks for verification.

Events

When the event occurs, Bongloy creates an Event object which contains the relevant information about what just happened, including the type of event and the data associated with that event. Bongloy then sends the Event object, via an HTTP POST request, to any endpoint URLs that you have createdin your account's Webhook endpoints page.

{
  "id": "3ddc184a-42c6-422e-9d39-8978d38afaa0",
  "object": "event",
  "created": 1549084295,
  "livemode": false,
  "data": {
    "object": {
      "id": "ecc28a2c-07d5-4504-b514-3a0ad1571cd0",
      "paid": true,
      "amount": 500,
      "object": "charge",
      "source": {
        "id": "96db6b0c-102e-435d-8ef9-32cbe234a5cf",
        "name": null,
        "brand": "UnionPay",
        "last4": "0005",
        "object": "card",
        "country": null,
        "created": 1549084204,
        "customer": null,
        "exp_year": 2019,
        "livemode": false,
        "cvc_check": "unchecked",
        "exp_month": 12,
        "address_zip": null,
        "fingerprint": "2c3f7ea106bccd749b5e97983ea18c2627935fe7b12662397187580e3957058d",
        "address_city": null,
        "address_line1": null,
        "address_line2": null,
        "address_state": null,
        "address_country": null,
        "address_zip_check": "unchecked",
        "address_line1_check": "unchecked"
      },
      "status": "succeeded",
      "created": 1549084291,
      "dispute": null,
      "refunds": {
        "url": "/v1/charges/ecc28a2c-07d5-4504-b514-3a0ad1571cd0/refunds",
        "data": [],
        "object": "list",
        "has_more": false
      },
      "captured": true,
      "currency": "USD",
      "customer": null,
      "livemode": false,
      "metadata": {},
      "refunded": false,
      "description": "Test live charge",
      "failure_code": null,
      "amount_refunded": 0,
      "failure_message": null,
      "balance_transaction": "272f810d-5df2-4a18-87a7-59082bc82564",
      "statement_descriptor": null
    }
  },
  "type": "charge.succeeded"
}

Configuring your Webhooks settings

Webhooks are configured in theDashboard's Webhook endpoints section. Click on the New button and fill in URL for the endpoint.

Receiving a webhook notification

Creating a webhook endpoint on your server is no different from creating any page on your website. With PHP, you might create a new .php file on your server; with a framework like Sinatra, you would add a new route with the desired URL.

Webhook data is sent as JSON in the POST request body. The full event details are included and can be used directly, after parsing the JSON into an Event object.

require 'json'

# Using Sinatra
post '/my/webhook/url' do
  # Retrieve the request's body and parse it as JSON:
  event_json = JSON.parse(request.body.read)

  # Do something with event_json

  status 200
end

Receiving webhooks with a CSRF-protected server

If you're using Rails, Django, or another web framework, your site might automatically check that every POST request contains a CSRF token. This is an important security feature that helps protect you and your users from cross-site request forgery attempts. However, this security measure might also prevent your site from processing legitimate webhooks. If so, you might need to exempt the webhooks route from CSRF protection.

Receiving webhooks with a HTTPS server

If you use an HTTPS URL for your webhook endpoint, Bongloy will validate that the connection to your server is secure before sending your webhook data. For this to work, your server must be correctly configured to support HTTPS with a valid server certificate.

Responding to a Webhook

To acknowledge receipt of a webhook, your endpoint should return a 2xx HTTP status code. All response codes outside of this range, including 3xx codes, will indicate to Bongloy that you did not receive the webhook. This means that a URL redirection or a "Not Modified" response will be treated as a failure. Bongloy will ignore any other information returned in the request headers or request body.

In case of failure, Bongloy will attempt to deliver your webhooks for up to five days with an exponential back off. Webhooks cannot be manually retried after this time.

Verifying a webhook

Bongloy signs the webhook events it sends to your endpoints. We do this by including a signature in each event's Bongloy-Signature header. This allows you to validate that the events were sent by Bongloy, not by a third party. Because Bongloy's API is compatible with Stripe's API you can verify signatures using Stripe's official libraries. You can also verify signatures manually using your own solution.

Verifying signatures using Stripe's official libraries

We recommend using one of Stripe libraries to verify signatures. You perform the verification by providing the event payload, the Bongloy-Signature header, and the endpoint's secret. If verification fails, Stripe library returns an error. Here's an example of how to verify a signature using Stripe's offical Ruby library.

# Verifying a Webhook signature in Ruby
# You can find your endpoint's signing secret in your webhook settings page
webhook_signing_secret = 'ZeNerJt...'

payload = request.body.read
bongloy_signature = request.env['HTTP_BONGLOY_SIGNATURE']

# if the signature is invalid a Stripe::SignatureVerificationError will be raised
event = Stripe::Webhook.construct_event(payload, bongloy_signature, webhook_signing_secret)

Preventing replay attacks

A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. To mitigate such attacks, Bongloy includes a timestamp in the Bongloy-Signature header. Because this timestamp is part of the signed payload, it is also verified by the signature, so an attacker cannot change the timestamp without invalidating the signature. If the signature is valid but the timestamp is too old, you can have your application reject the payload.

Stripe's official libraries have a default tolerance of five minutes between the timestamp and the current time. You can change this tolerance by providing an additional parameter when verifying signatures. We recommend that you use Network Time Protocol (NTP) to ensure that your server's clock is accurate and synchronizes with the time on Bongloy's servers.

Bongloy generates the timestamp and signature each time we send an event to your endpoint. If Bongloy retries an event (e.g., your endpoint previously replied with a non-2xx status code), then we generate a new signature and timestamp for the new delivery attempt.

Verifying signatures manually

Although we recommend using Stripe's official libraries to verify webhook event signatures, you can use the following steps to create a custom solution.

Step 1: Extract timestamp and signature from the header.

The Bongloy-Signature header contains a timestamp and signature. The timestamp is prefixed by t=, and the signature is prefixed by v1=. Here's an example:

"Bongloy-Signature": "t=1548737520, v1=e9094db9be3d8957e973471936b7ec41c82a24582f9d4751f57427430f47ce62"

Split the header, using the , character as the separator, to get a list of elements. Then split each element, using the = character as the separator, to get a prefix and value pair. The value for the prefix t corresponds to the timestamp, and the value for the prefix v1 corresponds to the signature. You can discard all other elements.

Step 2: Prepare the signed_payload string

signed_payload can be constructed by concatenating:

  • The timestamp (as a string)
  • The character .
  • The actual JSON payload (i.e. the request's body)
  • Step 3: Determine the expected signature

    Compute a HMAC with the SHA256 hash function. Use the endpoint's signing secret as the key, and use the signed_payload string as the message.

    Step 4: Compare signatures

    Compare the signature in the header to the expected signature. If a signature matches, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.

    To protect against timing attacks, use constant-time string comparison to compare the expected signature to each of the received signatures.

    Security

    Bongloy has been audited by a PCI-certified auditor and is certified to PCI Service Provider Level 1. This is the most stringent level of certification available in the payments industry. To accomplish this, we make use of best-in-class security tools and practices to maintain a high level of security at Bongloy.

    HTTPS and HSTS for secure connections

    Bongloy forces HTTPS for all services using TLS (SSL), including our public website and the Dashboard.

    • Bongloy.js is served only over TLS
    • Stripe's official libraries connect to Bongloy's servers over TLS and verify TLS certificates on each connection

    We use HSTS to ensure browsers interact with Bongloy only over HTTPS. Bongloy is also on the HSTS preloaded lists for both Google Chrome and Mozilla Firefox.

    Unofficial Sample Applications

    Below is a list of unofficial open source sample applications that work with Bongloy. If you would like your application listed below please contact us.

    Application Author(s)
    Ruby Demo Multiple
    Laravel Demo Khom Sovon
    iOS Demo Khom Sovon