Both IteroJS & billwerkJS are now SubscriptionJS
For compatibility reasons we still allow the usage of the old URLs, but do not count on them as they will be disabled sooner or later!
New URLs to include SubscriptionJS from are:
  • https://selfservice.billwerk.com/subscription.js for the production environment and
  • https://selfservice.sandbox.billwerk.com/subscription.js for the sandbox environment

Overview

SubscriptionJS offers a lightweight, easy-to-use gateway so you can implement your payment, signup and customer self-service forms seamlessly in your own website. SubscriptionJS doesn't have any external dependencies, weighs in at only a few kB, and doesn't meddle with your DOM (except for loading PSP-specific javascript, if required). Essentially, it's a Javascript SDK for billwerk that hides cross-origin complexity.

Because of its design, SubscriptionJS is compatible with any frontend framework such as AngularJS, EmberJS, knockout or simply jQuery.

Essentially, SubscriptionJS consists of these three modules:

Signup
Handles the signup of new customers. These operations can be performed for anonymous users and consequently, don't require any form of identification or authentication. Signup also provides functionality to calculate a detailed pricing information based on the cart and customer data (tax depends on country and vat id). A signup includes registration of the new customer and the subscription in billwerk.
For data security reasons the publically accessible signup does not check for existance of a customer. Consequently, all subsequent subscriptions of a single customer will create a new customer object.
If you want to allow customers to subscribe several times you have to provide a separate order process from within your customer's authenticated account.
Currently, SubscriptionJS does not provide a corresponding helper method. Please take a look at our REST API documentation on how to create such an order. The rest of the order process is covered in SubscriptionJS.
Portal
Contains methods that help you create a portal for your customers.
  • View current plan
  • Modify personal data: name, address, ...
  • Change payment data: provide new CC, change payment method, ...
  • Up-/Downgrade: SubscriptionJS provides helpers to commit an up-/downgrade order. The order has to be created using the billwerk API. Details
  • Order additional components (in conjunction with up-/downgrades)
  • Cancel a contract
  • Download invoices
Since these methods allow fetching / modifying contract information and personal data, they require a customerToken that you have to acquire through a regular API call from your backend. The token has a limited lifetime.
Please note all functions are contract based. You need to acquire a separate token for each contract.
Payment
Puts an abstraction on payment service providers. While this is the most complex module internally, it also has the simplest API. Usually, all you need to do is construct this object and pass it to the SubscriptionJS.Signup or SubscriptionJS.Portal methods that require payment.

Sandbox Environment

Billwerk provides two completely separate and independent billwerk instances, the Sandbox and the Live System.
Use the Sandbox for all your integration development. Please signup -> here <- for a free sandbox account.
After signup for testing purposes you can begin integration by loading SubscriptionJS like this:

<script type="text/javascript" src="https://selfservice.sandbox.billwerk.com/subscription.js"></script>
For payment processes you will also need to pass a public API key:
paymentService = new SubscriptionJS.Payment({ publicApiKey : "527cc4c951f45909c493c820" }...
This key can be found within the self service settings of your billwerk Admin UI.

Important! Please note the billwerk Sandbox is almost an exact copy of the Live System. This means you need to be careful with your test data. Emails will be sent and real payments can be triggered for some PSPs. Make sure to use email addresses you own and configure your PSPs to just simulate payments.

Subscriptions

Let's cut to the chase. Here's an example of a Marcellus Wallace signing up. Details will be dicussed in the following sections.

Load SubscriptionJS and initialize basic objects. For identification you need to provide your publicApiKey. The providerReturnUrl specifies a page on your website a customer returns to after payment on a payment provider page (e.g. PayPal).

<script type="text/javascript" src="https://selfservice.sandbox.billwerk.com/subscription.js"></script>
<script type="text/javascript">
    var signupService = new SubscriptionJS.Signup();
    var paymentService = new SubscriptionJS.Payment({ 
            publicApiKey : "527cc4c951f45909c493c820",
            providerReturnUrl : "https://your_domain.com/your_finalize_page"
        }, 
        function () { 
            /*Everything initialized so we can go on...  
              Start processing order, because example does not have a form.
              In real life you would probably set a flag that initialization is finished.*/
            createOrder();
        }, 
        function() { /*error*/ });
    // Important! SubscriptionJS.Payment not necessarily ready here. Use success callback to go on in order process
Important! SubscriptionJS.Payment does asynchronous initialization of the PSP specific JS code. Do not start order process until success callback is called!

The following section is hardcoding data that would usually be entered by the customer within a form. This is just for simplicity of the example.

    var cart = {
        "planVariantId": "527caacdeb596a247c6e0500"
    };
    var customer = {
        "firstName": "Marcellus",
        "lastName": "Wallace",
        "emailAddress": "mhw@example.com"
    };
    var paymentData = {
        "bearer": "CreditCard:Paymill",
        "cardNumber": "5169147129584558",
        "expiryMonth": "12",
        "expiryYear": "2017",
        "cardHolder": "Marcellus Wallace",
        "cvc": "311"
    };

The first step in the subscription process is creating an order. The function createOrder(...) takes the cart and customer data, as well as a success and error callback.

    //Step 1: Create order
    var createOrder = function()
    {
        signupService.createOrder(cart, customer, 
            pay, //Order created. Go on...
            errorHandler //Order could not be created
        );
    }

After successful creation of the order the payment process is triggered. The function paySignupInteractive(...) takess paymentService, paymentData and the order that was returned in the last step. You also pass a success and error callback.
This step is also done for free subscriptions. It'll simply finish the order process without triggering any payment.

In a real world integration step 1 (order creation) and 2 (order commit / payment) should be implemented on separate pages to be able to handle the order process within the context of a single order.
    //Step 2: Trigger payment / order commit
    var pay = function(order) {
        signupService.paySignupInteractive(paymentService, paymentData, order,
            paymentHandler, //Everything ok so far. Go on...
            errorHandler //Payment failed
        );
    };

If step 2 returned successfully there are two possible use cases.

  1. The payment could already be finished without a visible payment provider page.
  2. The payment process requires the customer to be forwarded to the payment provider page (e.g. PayPal checkout). After the payment PSPs checkout page the customer will be forwarded to the Url you specified during initialization in providerReturnUrl

    //Step 3: Forward to PSP page if not already finished
    var paymentHandler = function (result) {
        if (!result.Url)
            alert("success!"); //Successful subscription
        else
            window.location.href = result.Url; //Forward to PSP page to pay
    }

The rest of the example is a very simple error handler and a call executing the example. In a real world error handler you'd probably do handling based on the error code returned, e.g. let the user change credit card number if it was invalid. You can call paySignupInteractive several times if previous payments failed. Billwerk will take care that only a single successful payment is processed for an order.

Beware the user from double clicking the order button. Billwerk will still handle the payments correctly, but it would most probably produce undesired effects in your order process.
    var errorHandler = function(errorData)
    {
        alert("Subscription failed");
    };
    createOrder();

Finalize page

As shown in the last examples some payment provider integrations involve forwarding to the PSP's checkout page. The PSP will forward the user to the Url you provided in providerReturnUrl after the customer has finished checkout. This Url is called the finalize page. It is the last page of the order process. On this page you need to call the SubscriptionJS function finalize(...). The data structure passed to the functions is the same as in step 3 of the initial example.

SubscriptionJS.finalize(success,error);
Don't truncate the Url parameters used by the PSP. SubscriptionJS needs this information.

The simple approach

SubscriptionJS also provides a way to combine step 1 and 2 of the initial example. In this case you just need a single call, a success and an error handler.

signupService.subscribe(paymentService, cart,  customer, paymentData,
    function (subscribeResult) {
        if (!result.Url)
            alert("success!"); //Successful subscription
        else
            window.location.href = result.Url; //Forward to PSP page to pay
        },
    function (errorData) {
        alert("something went wrong!");
    }
);

Its simplicity may be tempting, but combining order creation and payment in a single step can lead to serious problems if not handled correctly. Customers could easily end up placing two or more orders by going back in the order process and clicking the order button a second or third time. Each click will produce a separate order and payment. You also can't offer a convenient way to change payment data, if the customer made a mistake. We strongly encourage you to use the approach with separated order creation and order commit / payment you saw in the first example.

Don't use the simple approach if the preferred approach of separated steps is applicable for you.

Portal

Please note all functions are contract based. You need to acquire a separate token, which has a limited lifetime, for each contract .

The portal contains methods that enables you to serve your customer a rich set of possibilities to view and/or change their data such as personal data, payment data and subscription data. To gather this data you need to initialize the portal constructor with the token. If the token isn't valid we return a 401 error. Given the token was valid you now have a portal object which offers various methods. Most important after initializing the constructor is gathering contract details via contractDetails(). This method returns all the data needed to enrich your customer portal.

var portal = new SubscriptionJS.Portal(token);
var contractDetails = portal.contractDetails(
    function(data){
        //Fill your portal with the received data
    },
    function(error){
        //Does whatever an error handler does, e.g. display an error message to the user
    }
);

Change Personal Data

You may want to give your customer the ability to change their personal data connected to all of their contracts. Using customerChange() will do the trick for you! It consumes customerData but only works if you explicitly allow it in Settings -> SubscriptionJS in the admin UI. Be aware that this method resembles a PUT, not a PATCH!
var customerData = {
    emailAddress : "john.doe@example.com",
    firstName : "John",
    lastName : "Doe",
    tag : "A53FD212-7879-44AD-923C-56DD20012100",
    companyName : "ACME, Inc.",
    vatId : "DE123465789",
    phoneNumber : "0123-45678",
    address : { 
        "addressLine1": "c/o Coworking Ltd.",
        "street": "Torstrasse",
        "houseNumber": "89a",
        "postalCode": "10123",
        "city": "Berlin",
        "country": "DE" 
    },
    locale : "en-US"
}

var customerChange = portal.customerChange(customerData,
    function(data){
        // Call contractDetails() again to get the updated data
    },
    function(error){
        //Does whatever an error handler does, e.g. display an error message to the user
    }
);

Change Payment Data

Besides changing the personal data, it's even more important to let the customer change his payment data himself, e.g. if payments are repeatedly failing. The portal offers therefore paymentChange() to set up a new payment method for the contract. Required are the SubscriptionJS payment service, as well as the new payment data.
var paymentService = new SubscriptionJS.Payment({
        publicApiKey: "5981a1e381b1fc126071xxxx",
        // For PSPs that rely on redirects (e.g. PayPal, Skrill, PayOne with 3D-Secure)
        providerReturnUrl: "https://yourReturnUrl.com"       
    },
    function (data) {
        //initialized
        var secretPaymentData = {
            "bearer": "Debit:PayOne",
            "accountHolder": "John Doe",
            "iban": "DE8512345678259910xxxx"
        }

        portal.paymentChange(paymentService, secretPaymentData,
        function (data){
            // Everything went well, this returns (may differ from PSP to PSP)
            //{
            //  "ContractId": "5cd186af443e551fe8e4xxxx",
            //  "CustomerId": "5cd183b8443e551e4446xxxx",
            //  "GrossTotal": 1
            //}
        },
        function (error){
            //Does whatever an error handler does, e.g. display an error message to the user
        } 
        );
    },
    function (error){
        //Does whatever an error handler does, e.g. display an error message to the user
    }
);

Payment for Up-/Downgrades

For security reasons, you can't actually perform the up/downgrade solely through the frontend. Instead, you'll have to create an order from your backend using the REST API. Please read here.

You'll want to ensure that up- or downgrades requested by the user are actually valid. For instance, a downgrade might not be allowed at this time or in conjunction with a given component that the customer is subscribed to. Since this type of business logic can generally be very complex, it makes sense to have the implementation on your side.

To pay for an up- or downgrade you are able to use two workflows:
  • Paying for an up-/downgrade synchronously
  • Paying for an up-/downgrade interactively

Synchronous

Allows the customer to pay for an upgrade using the currently active payment method in a 'synchronous' fashion, e.g. the success method will be called only after the call to the PSP has finished. This method is non-interactive, the user can't change the active payment method using this call.
var orderId = "5ce7eb000cefc277eaa3xxxx";
var paymentService = new SubscriptionJS.Payment({
    publicApiKey: "5981a1e381b1fc126071xxxx",
    providerReturnUrl: "https://yourReturnUrl.com"  // For PSPs that rely on redirects (e.g. PayPal, Skrill, PayOne with 3D-Secure)     
    },
    function (data) {
        //initialized
        var paymentChange = portal.upgradePaySync(orderId,
            function (data){
            // Everything went well and returns
            //{
            //  "OrderId": "5ce7eb000cefc277eaa3xxxx",
            //  "ContractId": "5cd186af443e551fe8e4xxxx",
            //  "CustomerId": "5cd183b8443e551e4446xxxx",
            //  "GrossTotal": 1395.67,
            //  "OrderStatus": "PaymentPending"
            //}
            },
            function (error){
                //Does whatever an error handler does, e.g. display an error message to the user
            } 
        );
    },
    function (error){
        //Does whatever an error handler does, e.g. display an error message to the user
    }
);

Interactive

Allows the customer to pay for an upgrade using new payment data. This method is interactive. Forward user to PSP page if URL is returned.
var order = { OrderId: "5ce7edfad5a49a0fdcc3xxxx", GrossTotal : 1677.53, Currency : "EUR"};
var paymentService = new SubscriptionJS.Payment({
    publicApiKey: "5981a1e381b1fc126071xxxx",
    providerReturnUrl: "https://yourReturnUrl.com"  // For PSPs that rely on redirects (e.g. PayPal, Skrill, PayOne with 3D-Secure)     
    },
    function (data) {
        //initialized
        var secretPaymentData = {
            "bearer": "CreditCard:PayOne",
            "cardNumber": "401200103714xxxx",
            "expiryMonth": "12",
            "expiryYear": "2022",
            "cardHolder": "John Doe",
            "cvc": "123"
        }

        var paymentChange = portal.upgradePayInteractive(paymentService, secretPaymentData, order,
            function (data){
            // Everything went well and now forward your use to the returned url
            //{
            //    "OrderId": "5ce7edfad5a49a0fdcc3xxxx",
            //    "GrossTotal": 1677.53,
            //    "Url": "https://secure.pay1.de/3ds/redirect.php?md=2808xxxx&txid=33531xxxx",
            //    "Currency": "EUR",
            //    "OrderStatus": "PaymentPending"
            //}
                if(data.Url){
                    window.location.href = data.Url; //Forward to PSP page to pay
                }
            },
            function (error){
                //Does whatever an error handler does, e.g. display an error message to the user
            } 
        );
    },
    function (error){
        //Does whatever an error handler does, e.g. display an error message to the user
    }
);

Cancel a contract

As sad as the news may be, a customer may want to cancel their contract with you. Given you've set this in Settings -> Self-Service -> Hosted Portal -> "Allow Contract Cancellation" the portal allows to make use of the method cancelContract(). It's simple to use as it contains only a success callback as well as an error callback and returns an object consisting of a message and an effective end date. The end date is set to the next possible end date.
{
  "Message": "Contract Cancelled",
  "EffectiveEndDate": "2020-04-04T09:40:13.0040000Z"
}

Payments / Payment Service Providers (PSPs)

Several customer interactions involve payments. billwerk offers integration with different payment providers. SubscriptionJS hides most of the complexity so integrating any supported providers is done with the same approach.

We also offer a payment method that does not involve any payment provider. On account payments can be used if a customer should be able to transfer money manually from his bank account after an invoice was received. To use this payment method you need to pass "InvoicePayment" as payment provider.

If a customer should not be required to pass payment information upon subscription, you can pass "None:None" as payment provider. This is only allowed for plan variants which permit subscriptions without payment information (flag 'allow signup without payment information'). A use case could be that you offer a trial period and want the user to pass payment information not until trial period has expired.
In some use cases it could also be suitable for up-/downgrades.

Nevertheless, let's begin with a basic payment workflow with a transparent PSP.

We distinguish between 'interactive' and 'non interactive' payments. 'Interactive' means a customer interactively enters his payment data like credit card or bank account information. In billwerk the most prominent use case for this kind of payment is the initial signup.
Usually upgrading a subscription involves a 'Non Interactive' payment. Payment information has already been provided during signup and will be reused for the upgrade.

'Interactive' payments

Interactive payments require two objects. We need to create an SubscriptionJS.Payment instance and an object representing the payment data. The payment data object contains the selected payment method / provider and additional payment information like credit card data, if required for the payment method.

paymentService = new SubscriptionJS.Payment({ publicApiKey : "527cc4c951f45909c493c820" }, 
    function () { /*ready*/ }, 
    function() { /*error*/ });
var paymentData = {
    "bearer": "CreditCard:Paymill",
    "cardNumber": "5169147129584558",
    "expiryMonth": "12",
    "expiryYear": "2015",
    "cardHolder": "Marcellus Wallace",
    "cvc": "911"
};
    
Both objects just need to be passed to the corresponding SubscriptionJS method representing the desired action.

Paying a subscription signup

signupService.paySignupInteractive(subscriptionJSPayment, secretPaymentData, order, success, error);

Paying an upgrade interactively

portalService.upgradePayInteractive(paymentService, paymentData, order, success, error);

Changing the payment method

portalService.paymentChange(paymentService, paymentData, success, error);

Credit Card Data, PCI-DSS

As you can see from the sample, the credit card data will be known to your own javascript. Make sure that this information is not logged and never sent to your server! SubscriptionJS will hand off any PCI-DSS protected data to the selected PSP so the data is sent from the customer's browser directly to the PSP, thus keeping you from PCI-DSS hassle.

Of course, it is important to deliver the form itself via HTTPS to prevent third parties from tampering with the form. SubscriptionJS is also only available via HTTPS.

Integrating payment processes with PSP redirects

This part is mandatory for black-label and white-label providers. Even though white-label providers are usually transparent, there are processes that include redirects to the PSP, e.g. credit card payments with 3D secure. Let's take a look at another signup process. This time we want to integrate PayPal.

paymentService = new SubscriptionJS.Payment({
        publicApiKey : "527cc4c951f45909c493c820", 
        providerReturnUrl : "https://your_domain.com/your_finalize_page"
    }, 
    function () { /*ready*/ }, 
    function() { /*error*/ }
);
var paymentData = {
    "bearer": "PayPal",
    "emailAddress": "test@example.com" /*If not passed, billwerk will use the customer's signup email address*/
};
signupService.subscribe(paymentService, cart,  customer, paymentData, 
    function(data) {
        if (data.Url) {
            // Open the PSP URL if provided
            window.location.href = data.Url;
        }
        else {
            // No PSP page to open here
        }
    }
, error);
First please have a look at the success callback in this example. If calling subscribe() succeeded it will return a URL that leads to the PSP checkout page. Open it to let the customer go on with the payment.
Now take a look at the initialization of SubscriptionJS.Payment. There is an additional parameter named providerReturnUrl. This URL is passed to the PSP. When the customer finished his payment he might be redirected to this URL. This page is used to finalize the order and show the customer a succes or error message. The required SubscriptionJS code on this page is a single call:
SubscriptionJS.finalize(success,error);
Except for the mandatory success and error callback no parameters need to be passed. The PSP added some URL encoded parameters which SubscriptionJS passes to the billwerk server. It'll trigger order finalization as subscribe() would do for white label providers.
It is not certain that the order process ever reaches the finalize page, e.g. a customer could simply close the browser after successful payment. Therefore, as a second way of payment confirmation billwerk is notified by the PSP which will trigger the same processes as SubscriptionJS.finalize().

'Non Interactive' payments

A good example is upgrading a subscription. Although SubscriptionJS provides a way to pay upgrades interactively, usually payment is done implicitely with the payment bearer stored for the subscription.

    portalService.upgradePaySync(orderId,success,error);
As you can see for non interactive payments no payment information needs to be passed.
Non interactive payments are also triggered by billwerk transparently for each recurring payment.

Payment Form Iframe

Dealing with PCI DSS compliance can cause a real headache. billwerk therefore provides an easy way to be compliant without the hustle to deal with it yourself for your business. You'll find further information about our paymentForm here.

Coupons

To grant a discount you can offer Coupon Codes to your customers. A single Coupon Code can be passed with each subscription or up-/downgrade order.

A Coupon Code is passed in the Cart object.

var cart = {
    "planVariantId": "527caacdeb596a247c6e0500",
    "couponCode": "ACB-123"
};
        

Coupon Code validation

The Cart object can be passed several SubscriptionJS methods. If the Coupon Code is invalid an error with the code InvalidCouponCode will be returned.

The Preview() method is an exception. It'll return an Order object in either case. It contains the sub object Coupon with detailed information.

Valid Code passed to Preview()

{
    "Order" : {
        "Coupon":{
            "CouponCode":"ABCDEF",
            "CouponId":"55d31a7d68a44f270478425d",
            "DiscountId":"55d31a4e68a44f2704784259"
        },
        ...
    }
}
        

Invalid Code passed to Preview()

{
    "Order" : {
        "Coupon":{
            "CouponCode":"ABCDEF",
            "ErrorMessage":"CouponCode is invalid",
            "ErrorCode":"InvalidCouponCode"
        },
        ...
    }
}
        
Coupon Codes are normalized to uppercase alphanumeric values before beeing validated. This means that a customer might enter 'ABc*12-3' which will be normalized to 'ABC123'. The same is done with the codes specified in the billwerk Admin UI. This means a specified code 'AB-C1-23' will match the customer's code 'ABc*12-3'.

Synchronization

Your system needs to be kept in sync with all changes processed via customer self service.

To keep your system synchronized please pay attention to the following advices:

  • Never rely on the customer's workflow will ever reach the last step, e.g. the customer might have closed the browser before success page was loaded, or the final response might have been lost in the vasts of internet. You need to face this by consuming our Webhooks and trigger subsequent processes on your side.
  • Do not presume your customer will finish a process, e.g. do not create a customer in your database before you got notified by billwerk with the AccountCreated webhook showing the signup was finished successfully.

Popular Use Cases

We gathered some popular use cases in our UseCases-Section for you: UseCases

Going Live

From the technical point of view you need to make sure to load SubscriptionJS from the Live Instance of billwerk

<script type="text/javascript" src="https://selfservice.billwerk.com/subscription.js"></script>
Get your publicApiKey from the live settings and update your code accordingly.
paymentService = new SubscriptionJS.Payment({ publicApiKey : "527cc4c951f45b08c493d834" }...
Also update your plan variant ids and component ids.

When done with the technical todos make sure the PSP settings on the Live System are ready to do real payments. Make at least one real life test for each supported payment method.