Skip to content
Last updated

Introduction

This documentation describes how to integrate with a unified and secure payment system for initiating payments or tokenizing payment instruments. The approach follows a two-step flow:

  1. Obtain a concludable payment request token – This token represents either an authorized payment (ready for capture) or an authorized payment instrument for future use. To obtain it, the frontend mounts the Universal Payment Component (UPC) using a short-lived user payment session token securely obtained from the backend.

  2. Post a usage with the obtained payment request token – The token can be used to create a payment instrument in the customer account, attach it to a contract, or post a usage (e.g., credit top-up, ticket purchase). If the usage involves an amount greater than zero, capture is deferred until the related good or service is booked, minimizing refunds. If unused within its validity period, the authorization is cancelled or refunded.

Key Concepts

  • Universal Payment Component (UPC) – Embeddable JavaScript component handling the authorization of a payment request token.
  • Universal Payment Gateway (UPG) – Public API the UPC communicates with, authorized via a short-lived session token.
  • Payment Instrument – A user-specific object used to attempt a payment.
  • Payment Method – The type of payment instrument (e.g., credit card, direct debit, PayPal).
  • Scope – Defines the payment context (MEMBER_ACCOUNT or ECOM) to determine available payment methods.

Important Usage Notes

  • Always define either finionPayCustomerId or customerId for an existing customer session. When assigning a paymentRequestToken to a usage, the system checks that the token belongs to the correct customer. If this check fails, the operation will not succeed.
  • Example: Selling a contract online and collecting a payment method for the upfront fee requires two paymentRequestTokens — one for the payment instrument and one for the actual upfront payment. This means creating two separate user payment sessions. If the flow is for a new customer, create the second session using the finionPayCustomerId returned by the first.
  • While the paymentRequestToken is unused for posting a usage, no funds are captured (if the payment method allows). Authorizing a token with amount > 0 only authorizes the payment; capture happens when posting the usage. This prevents unnecessary collections or refunds in case of process errors or user cancellation.
  • Saving payment methods: If the scope is ECOM and the payment method supports saving, the user can choose to store the method for future use.
  • Authorizing saved payment methods: Stored payment methods are already authorized, so they are not re-authorized when selected via the component. The payment result is returned upon posting the usage.
  • Any unused paymentRequestToken is automatically cancelled when the related user payment session expires.
  • A user payment session is automatically invalidated once one paymentRequestToken from that session is used — only one token per session can be used.

Endpoints Using paymentRequestToken

The paymentRequestToken returned by the UPC can be used in the following scenarios:

Creating a Payment Instrument in the Customer

  • Create a payment instrument and link it to the customer so it can be used in future payment runs (e.g., membership fees).
  • Applies to:
    • Creating a new customer and contract (work in progress)
    • Adding a contract to an existing customer (work in progress)
    • Offering self-service payment method updates (work in progress)
    • Adding a secondary payment method (planned)

Posting a Sellable Entity

If the paymentRequestToken is authorized with a payment amount, it can be used for purchasing any sellable entity:

  • Upfront payment in contract creation (joining fee or total contract value) (work in progress)
  • Account balancing for open fees (work in progress)
  • Purchasing a day ticket (work in progress)
  • Purchasing a value voucher (planned)
  • Purchasing a contract voucher (planned)
  • Purchasing a course contingent (planned)
  • Purchasing an appointment (e.g., personal training contingent) (planned)

Creating a User Payment Session

To initiate a payment process or capture a payment instrument, you must first create a user payment session.

Endpoint: POST /v1/payments/user-session

Required Scope: PAYMENT_WRITE

Description: This request generates a short-lived token used by the UPC to authenticate payment flows. It can be for immediate transactions or for storing payment instruments for future recurring payments.

Request Body Parameters

amountnumberrequired

Specifies the payment amount for the initiated transaction. Should equal 0 to capture a payment instrument for future recurring payments. The currency is defined by the studio.

Example: 19.99
scopestringrequired

Specifies where the created payment instruments will be used, as the available payment methods differ by scope.

Enum ValueDescription
MEMBER_ACCOUNT

Use when initiating a payment user session to collect a payment instrument intended for future payment runs (e.g., BACS, credit card).

ECOM

Use when the user is making a purchase or when the payment instrument will be used for future user-initiated payments (e.g., saving a credit card for later purchases).

Example: "MEMBER_ACCOUNT"
customerIdinteger(int64)

This field represents the unique identifier for an existing customer within ERP. Providing this ID ensures the payment session is linked to the correct customer record. It is a mutually exclusive field with finionPayCustomerId, meaning you can only provide one or the other.

  • Conditions for use: This ID is required for payment sessions involving existing customers.

  • Behavior when omitted: If this field is left empty, a new customer will be treated as a “potential customer” and a finionPayCustomerId will be automatically generated and returned in the response. If omitted, it will not be possible to use it for existing customers.

  • Mutually exclusive with: finionPayCustomerId

Example: 1234567890
finionPayCustomerIdstring(uuid)

This field is the identifier for a customer within the Finion Pay payment service, typically used for customers who are not yet registered in ERP. Use this ID to track repeat payment sessions for a potential customer.

  • Conditions for use: This ID should only be provided for subsequent payment sessions for a customer who has been previously identified by Finion Pay but doesn’t have an ERP customerId yet.

  • Behavior when omitted: In the absence of a customerId, a new finionPayCustomerId will be automatically created and assigned to the user for the current session.

  • Mutually exclusive with: customerId

Example: "753ea8ec-c2ec-4761-824b-bc46eda3f644"
permittedPaymentChoicesArray of stringsunique

List of permitted payment choices, i.e. obtained by the contract offer. Acts as a filter for the available payment methods defined by the scope

Items Enum ValueDescription
BACS

Payment by BACS direct debit

IDEAL

Payment by ideal

PAYPAL

Payment by paypal

TWINT

Payment by Twint

SEPA

Payment by SEPA direct debit

BANK_TRANSFER

Payment by bank transfer

CH_DD

Payment by CH_DD direct debit

CASH

Payment by cash

BANCONTACT

Payment by bancontact

CREDIT_CARD

Payment by credit card

Example: ["CASH"]
referenceTextstringrequired

Allows the definition of the reference text shown on the bank statement of the customer.

Example: "Gym Joining Fee 01.07.2025"

Example Request

curl -i -X POST \
  https://open-api-demo.open-api.magicline.com/v1/payments/user-session \
  -H 'Content-Type: application/json' \
  -H 'X-API-KEY: YOUR_API_KEY_HERE' \
  -d '{
    "amount": 19.99,
    "scope": "MEMBER_ACCOUNT",
    "customerId": 1234567890,
    "finionPayCustomerId": "753ea8ec-c2ec-4761-824b-bc46eda3f644",
    "permittedPaymentChoices": [
      "CASH"
    ],
    "referenceText": "Gym Joining Fee 01.07.2025"
  }'

Response body

tokenstringrequired

The token for the user session.

Example: "CllClFmVlSCs3oe0ND0JloLWlNzdb3QseU4507gf1mSVAHqRTwzKWU"
tokenValidUntilstring(date-time)required

The date and time until the token is valid.

Example: "2025-01-07T16:25:09.416924Z"
finionPayCustomerIdstring(uuid)required

Identifies a customer in Finion Pay, i.e. to retreive existing payment instruments.

Example: "753ea8ec-c2ec-4761-824b-bc46eda3f644"

The token returned is the userSessionToken required to initialize the UPC in your frontend integration.


Payment Widget Integration Guide

An embeddable payment interface that can be integrated into any web application.

The following URIs are available:

Quick Start

<script src="INSERT_WIDGET_URI_HERE"></script>
<div id="payment-widget"></div>

<script>
const widget = window.paymentWidget.init({
    userSessionToken: 'your-session-token',
    environment: 'live',
    countryCode: 'US',
    locale: 'en',
    container: 'payment-widget'
});

// Clean up when done
widget.destroy();
</script>

Configuration

ParameterTypeDescription
userSessionTokenstringUser session token
environment`'test''sandbox''live'`Payment environment
countryCodestringISO country code (e.g., 'US')
localestringLocale (e.g., 'en')
container`stringHTMLElement`Element ID or element reference

Optional:

  • styling - Custom theme colors and styling
  • i18n - Translation overrides
  • onSuccess - Success callback function
  • devMode - Show i18n keys instead of translated text (development only)

Customization

Styling:

styling: {
    primaryColor: '#007bff',
    textColorMain: '#333333',
    borderRadius: '4px'
}

Translations:

i18n: {
    'upc.my.payment.instruments': 'My Payment Methods',
    'upc.payment.methods.add.new': 'Add New Payment Method'
}

Development Mode:

devMode: true // Shows i18n keys instead of translations for development

Integration Examples

React Integration

import React, { useEffect, useRef } from 'react';

export const PaymentWidget = ({ userToken, onPaymentSuccess }) => {
    const containerRef = useRef(null);
    const widgetRef = useRef(null);

    useEffect(() => {
        if (containerRef.current && window.paymentWidget) {
            widgetRef.current = window.paymentWidget.init({
                userSessionToken: userToken,
                environment: 'live',
                countryCode: 'US',
                locale: 'en',
                container: containerRef.current,
                onSuccess: onPaymentSuccess
            });
        }

        return () => widgetRef.current?.destroy();
    }, [userToken, onPaymentSuccess]);

    return <div ref={containerRef} />;
};

Angular Integration

import { Component, ElementRef, ViewChild, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-payment-widget',
  template: '<div #paymentContainer></div>'
})
export class PaymentWidgetComponent implements OnDestroy {
  @ViewChild('paymentContainer', { static: true }) containerRef!: ElementRef;
  private widget: any;

  ngAfterViewInit() {
    const sessionToken = sessionStorage.getItem('paymentSessionToken') || this.getUserToken();
    sessionStorage.setItem('paymentSessionToken', sessionToken);

    this.widget = window.paymentWidget.init({
      userSessionToken: sessionToken,
      environment: 'live',
      countryCode: 'US',
      locale: 'en',
      container: this.containerRef.nativeElement,
      onSuccess: (token) => {
        sessionStorage.removeItem('paymentSessionToken');
        this.handlePaymentSuccess(token);
      }
    });
  }

  ngOnDestroy() {
    this.widget?.destroy();
  }
}

Vue.js Integration

<template>
  <div ref="paymentContainer"></div>
</template>

<script>
export default {
  name: 'PaymentWidget',
  props: ['userToken'],
  mounted() {
    const sessionToken = sessionStorage.getItem('paymentSessionToken') || this.userToken;
    sessionStorage.setItem('paymentSessionToken', sessionToken);

    this.widget = window.paymentWidget.init({
      userSessionToken: sessionToken,
      environment: 'live',
      countryCode: 'US',
      locale: 'en',
      container: this.$refs.paymentContainer,
      onSuccess: (token) => {
        sessionStorage.removeItem('paymentSessionToken');
        this.$emit('paymentSuccess', token);
      }
    });
  },
  beforeUnmount() {
    this.widget?.destroy();
  }
}
</script>

Handling Redirects

For 3D Secure authentication, users may be redirected to their bank. The widget automatically detects and resumes payment processing after redirect.

function initializeWidget() {
    const userSessionToken = sessionStorage.getItem('paymentSessionToken') || getCurrentUserToken();
    if (!sessionStorage.getItem('paymentSessionToken')) {
        sessionStorage.setItem('paymentSessionToken', userSessionToken);
    }
    const widget = window.paymentWidget.init({
        userSessionToken: userSessionToken,
        environment: 'live',
        countryCode: 'US',
        locale: 'en',
        container: 'payment-widget',
        onSuccess: (token) => {
            sessionStorage.removeItem('paymentSessionToken');
            handlePaymentSuccess(token);
        }
    });
}
initializeWidget();

Error Handling

Common validation errors:

  • Container element not found
  • Missing required parameters
  • Invalid environment value
try {
    const widget = window.paymentWidget.init(config);
} catch (error) {
    console.error('Widget initialization failed:', error.message);
}

API Reference

interface PaymentWidget {
    init(config: PaymentConfig): { destroy(): void };
}

interface PaymentConfig {
    userSessionToken: string;
    environment: 'test' | 'sandbox' | 'live';
    countryCode: string;
    locale: string;
    container: string | HTMLElement;
    styling?: {
        primaryColor?: string;
        textColorMain?: string;
        borderRadius?: string;
    };
    i18n?: Record<string, string>;
    onSuccess?: (paymentRequestToken: string) => void;
    devMode?: boolean;
}