Skip to main content
More in Learn

Two Payment Integration

Two is a Buy Now, Pay Later payment solution for businesses. Once a purchase is made via Two, the invoice is sent directly to the selected organisation’s accounting system using EHF. Two is currently available in the United Kingdom and Norway, with plans to launch in Sweden soon.

Two Credentials

To get started with Two, you will need to sign up for their services by creating an account. After signing up, you will receive an email containing the test credentials. Alternatively, you can also find the credentials under the “Developer tools” section in the dashboard. As soon as you are ready to go into production, you can contact Two to receive the production keys instantly.

Initiating Payment

The typical flow being followed in the SaaS boilerplate we have is as follows:

  • Using the Two Search API, the user can enter their company details. Two checks whether an invoice can be offered to the company.
  • The next step is verifying the user’s identity. If the user is based in Norway, this is done via Vipps.
  • Lastly, when the order is accepted, the user receives an invoice made out to their company that can be paid as per convenience.

Two Search API

The first step is the user searching for their company so Two can check whether sending an invoice is an option or not. This is done via their Search API. The API is location specific so you need to specify the country code as follow:

Norway
https://no.search.two.inc/

UK
https://gb.search.two.inc/

It takes in three required parameters:

  • limit: total number of companies you’d like to be returned
  • offset: starting index of results in an array of matches
  • q: the query string

A request to the Search API would then look like the code snippet below.

const query = new URLSearchParams({
  limit: 'string',
  offset: 'string',
  q: 'string'
}).toString();

const resp = await fetch(
  `https://no.search.two.inc/search?${query}`,
  {method: 'GET'}
);

const data = await resp.text();

Create Order

After the company verification, a user is then redirected to pay via invoice at Two. To create an order there, you will need to send a POST request to the /order endpoint with all the order related information.

const resp = await fetch(
  `https://sandbox.api.two.inc/v1/order`,
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'string'
    },
    body: JSON.stringify({
      currency: 'GBP',
      invoice_type: 'DIRECT_INVOICE',
      gross_amount: '400',
      net_amount: '360',
      discount_amount: '0.0',
      discount_rate: '0.00',
      tax_amount: '40',
      tax_rate: '0.01',
      buyer: {
        representative: {
          first_name: 'John',
          last_name: 'Doe',
          phone_number: '12345678',
          email: 'example@example.inc'
        },
        company: {
          country_prefix: 'GB',
          organization_number: '13078389',
          company_name: 'TWO B2B LTD'
        }
      },
      line_items: [
        {
          type: 'PHYSICAL',
          name: 'Aluminium Plant',
          description: 'This is a plant',
          discount_amount: '0.00',
          gross_amount: '200.00',
          net_amount: '180.00',
          quantity: 5,
          unit_price: '0.00',
          tax_amount: '20',
          tax_rate: '0.1',
          tax_class_name: 'VAT 25%',
          quantity_unit: 'pcs',
          image_url: 'https://www.exampleobjects.com/product-image-1200x1200.jpg',
          product_page_url: 'https://www.example.com/products/f2a8d7e34',
          details: {
            brand: 'testbrand',
            categories: ['cat1', 'testcategory'],
            barcodes: [
              {
                value: '1234',
                type: 'testbarcode'
              }
            ],
            part_number: 'testnumber'
          },
          prototype_id: 'bd06d90d-fdef-4d6b-9438-11f160f219ac'
        },
        {
          type: 'PHYSICAL',
          name: 'Aluminium Plant',
          description: 'This is a plant',
          discount_amount: '0.00',
          gross_amount: '200.00',
          net_amount: '180.00',
          quantity: 5,
          unit_price: '0.00',
          tax_amount: '20',
          tax_rate: '0.1',
          tax_class_name: 'VAT 25%',
          quantity_unit: 'pcs',
          image_url: 'https://www.exampleobjects.com/product-image-1200x1200.jpg',
          product_page_url: 'https://www.example.com/products/f2a8d7e34',
          details: {
            brand: 'testbrand',
            categories: ['cat1', 'testcategory'],
            barcodes: [
              {
                value: '1234',
                type: 'testbarcode'
              }
            ],
            part_number: 'testnumber'
          },
          prototype_id: 'bd06d90d-fdef-4d6b-9438-11f160f219ac'
        },
        {
          type: 'PHYSICAL',
          name: 'Aluminium Plant',
          description: 'This is a plant',
          discount_amount: '0.00',
          gross_amount: '200.00',
          net_amount: '180.00',
          quantity: 5,
          unit_price: '0.00',
          tax_amount: '20',
          tax_rate: '0.1',
          tax_class_name: 'VAT 25%',
          quantity_unit: 'pcs',
          image_url: 'https://www.exampleobjects.com/product-image-1200x1200.jpg',
          product_page_url: 'https://www.example.com/products/f2a8d7e34',
          details: {
            brand: 'testbrand',
            categories: ['cat1', 'testcategory'],
            barcodes: [
              {
                value: '1234',
                type: 'testbarcode'
              }
            ],
            part_number: 'testnumber'
          },
          prototype_id: 'bd06d90d-fdef-4d6b-9438-11f160f219ac'
        }
      ],
      merchant_order_id: '1232',
      merchant_urls: {
        merchant_confirmation_url: 'http://localhost:6000/confirmation'
      },
      billing_address: {
        organization_name: 'Test',
        street_address: 'test2',
        postal_code: '1237',
        city: 'Oslo',
        region: 'test',
        country: 'NO'
      },
      shipping_address: {
        organization_name: 'Test',
        street_address: 'test2',
        postal_code: '1237',
        city: 'Oslo',
        region: 'test',
        country: 'NO'
      }
    })
  }
);

const data = await resp.json();

Some of these fields are required while some aren’t. To get a more comprehensive idea regarding the fields, head over to Two’s official documentation. (access required)

Order Confirmation

After the purchase has been confirmed, the user will be redirected back to the shop for a confirmation message. This redirect URL can be specified in the POST request made to the order endpoint. The endpoint to confirm the order takes in the order ID as a parameter. If the returned result says confirmed, this is also where you would ideally create an order in Crystallize.

const id = 'YOUR_id_PARAMETER';
const resp = await fetch(
  `https://sandbox.api.two.inc/v1/order/${id}/confirm`,
  {
    method: 'POST',
    headers: {'X-API-Key': 'string'}
  }
);

const data = await resp.text();

Next steps

What happens now is totally up to how you run your business. We advice you to set up proper fulfilment pipelines to orchestrate the orders, which you can use to manage the order fulfilment in a structured and automated way.

Read more about orders and fulfilment or watch our live stream where we demonstrate how Vipps payments work in our boilerplate.

People showing thumbs up

Need further assistance?

Ask the Crystallize team or other enthusiasts in our slack community.

Join our slack community