# Working with Payment API ## 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](/apis/magicline/openapi/openapi#operation/userSession) **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 ### Example Request ### Response body 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: - [https://widget.dev.payment.sportalliance.com/widget.js](https://widget.dev.payment.sportalliance.com/widget.js) - [https://widget.sandbox.payment.sportalliance.com/widget.js](https://widget.sandbox.payment.sportalliance.com/widget.js) (soon) ### Quick Start ```html
``` ### Configuration | Parameter | Type | Description | | | | --- | --- | --- | --- | --- | | `userSessionToken` | `string` | User session token | | | | `environment` | `'test' | 'sandbox' | 'live'` | Payment environment | | `countryCode` | `string` | ISO country code (e.g., 'US') | | | | `locale` | `string` | Locale (e.g., 'en') | | | | `container` | `string | HTMLElement` | 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:** ```javascript styling: { primaryColor: '#007bff', textColorMain: '#333333', borderRadius: '4px' } ``` **Translations:** ```javascript i18n: { 'upc.my.payment.instruments': 'My Payment Methods', 'upc.payment.methods.add.new': 'Add New Payment Method' } ``` **Development Mode:** ```javascript devMode: true // Shows i18n keys instead of translations for development ``` ### Integration Examples #### React Integration ```tsx 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
; }; ``` #### Angular Integration ```typescript import { Component, ElementRef, ViewChild, OnDestroy } from '@angular/core'; @Component({ selector: 'app-payment-widget', template: '
' }) 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 ```vue ``` ### Handling Redirects For 3D Secure authentication, users may be redirected to their bank. The widget automatically detects and resumes payment processing after redirect. ```javascript 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 ```javascript try { const widget = window.paymentWidget.init(config); } catch (error) { console.error('Widget initialization failed:', error.message); } ``` ### API Reference ```typescript 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; onSuccess?: (paymentRequestToken: string) => void; devMode?: boolean; } ```