Skip to content

Integrate · API Integration

Build the entire checkout and call the API directly

Process payments with RESTful endpoints and keep full control over the payment flow and UI. Card data is encrypted with your merchant public key before it is sent.

1 Your checkout page Built by you · collects card details
2 Your backend Built by you · encrypts the card data In your PCI scope
3 Finrelay API POST /api/transactions/authorize PCI DSS
Finrelay PCI DSS environment Your systems

What your customer sees

Everything the customer sees is yours. Finrelay renders no UI; your backend encrypts the card details and sends them to the API.

Your server → POST /api/transactions/authorize
{
  "amount": 4990,
  "currency": "EUR",
  "payment_method": {
    "type": "card",
    "data": {
      "encrypted_card_number": "base64_encrypted_value",
      "encrypted_cvv": "base64_encrypted_value",
      "encrypted_expiration_month": "base64_encrypted_value",
      "encrypted_expiration_year": "base64_encrypted_value"
    }
  },
  "return_url": "https://shop.example/return"
}

Illustrative preview with test data.

  1. Your card formYou build and host every field, including card number and CVV.
  2. Encrypted requestYour backend encrypts the card number, CVV and expiry with your merchant public key before sending them. Your integration must meet PCI DSS requirements.
  3. 3-D Secure and resultSend browser_info and a return_url for 3-D Secure, then check status and approved in the response.

Compare integrations

How the three integrations differ.

Hosted Payment Page Embedded Fields API Integration
Checkout runs on Finrelay Your page Your page
PCI DSS scope Lowest Reduced Full
Customization Logo, colours, language CSS variables Full UI control
Effort Lowest Medium Highest

Integrate in four steps

Get an access token

Use the OIDC Client Credentials flow with your client ID and secret.

Encrypt the card data

Encrypt card number, CVV, expiration month and year with your merchant public key.

Authorize the payment

Send the encrypted card, browser_info and a return_url to /api/transactions/authorize.

Handle the result

Check status and approved, complete 3-D Secure when required, and use webhooks for status updates.

POST /api/transactions/authorize
curl -X POST https://api.finrelay.io/api/transactions/authorize \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "reference": "ORDER-123456",
    "terminal_id": "TERM001",
    "description": "Order payment",
    "amount": 4990,
    "currency": "EUR",
    "transaction_type": "PURCHASE",
    "payment_method": { "type": "card", "data": { "encrypted_card_number": "..." } },
    "browser_info": { "...": "..." },
    "return_url": "https://shop.example/return"
  }'
POST /api/transactions/authorize
const res = await fetch('https://api.finrelay.io/api/transactions/authorize', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    reference: 'ORDER-123456',
    terminal_id: 'TERM001',
    description: 'Order payment',
    amount: 4990, // minor units
    currency: 'EUR',
    transaction_type: 'PURCHASE',
    payment_method: { type: 'card', data: encryptedCard },
    browser_info: browserInfo,
    return_url: 'https://shop.example/return',
  }),
});

const result = await res.json();

API Integration

Direct API integration allows you to process payments programmatically using RESTful endpoints. This approach gives you full control over the payment flow and UI/UX.


Authentication

All API requests require a valid OAuth 2.0 access token:

HTTP Headers

Authorization: Bearer <access_token>
Content-Type: application/json

Obtaining an Access Token

Use the Client Credentials flow to obtain an access token. See Obtain Access Token for detailed instructions.

Your client_id and client_secret are available in your merchant dashboard.


Processing Card Payments

Endpoint

POST /api/transactions/authorize

Data Encryption

All sensitive card data must be encrypted using your merchant public key before sending to the API.

Fields requiring encryption: - Card number - CVV - Expiration month - Expiration year

See the Encryption Example for detailed encryption instructions.

Card Payment Data Fields

Field Type Required Encrypted Description
encrypted_card_number string Yes Yes Card PAN (digits only, no spaces)
encrypted_cvv string Yes Yes Card security code (digits only)
encrypted_expiration_month string Yes Yes Expiration month (1–12, no leading zero)
encrypted_expiration_year string Yes Yes Expiration year — 2-digit (26) or 4-digit (2026); must be current or future year

Basic Request Example

{
  "reference": "ORDER-123456",
  "terminal_id": "TERM001",
  "description": "Order payment",
  "currency": "EUR",
  "amount": 10000,
  "transaction_type": "PURCHASE",
  "payment_method": {
    "type": "card",
    "data": {
      "encrypted_card_number": "base64_encrypted_value",
      "encrypted_cvv": "base64_encrypted_value",
      "encrypted_expiration_month": "base64_encrypted_value",
      "encrypted_expiration_year": "base64_encrypted_value"
    }
  },
  "customer": {
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com",
    "phone": "+1234567890",
    "address": "123 Example Street",
    "city": "Sampletown",
    "country": "BA",
    "postal_code": "12345"
  },
  "browser_info": {
    "user_agent": "Mozilla/5.0...",
    "accept_header": "text/html,application/xhtml+xml...",
    "java_enabled": false,
    "color_depth": 24,
    "screen_height": 1080,
    "screen_width": 1920,
    "time_zone_offset": -120,
    "language": "en-US"
  },
  "metadata": {},
  "return_url": "https://merchant.example.com/return"
}

Network Token Payments (Apple Pay / Google Pay)

For merchants who handle wallet token decryption on their own infrastructure, the network-token payment method type allows you to send pre-decrypted network token data (DPAN, cryptogram, ECI) through the gateway.

This is useful for:

  • Apple Pay integrations where you decrypt the PKPaymentToken server-side
  • Google Pay integrations with CRYPTOGRAM_3DS or PAN_ONLY authentication
  • Direct TSP (Token Service Provider) integrations

Data Encryption

The following fields must be encrypted using your merchant public key (same key used for card encryption):

  • Token number (DPAN)
  • Expiration month
  • Expiration year
  • Cryptogram (when present)

The eci and source fields are sent as plain text.

See the Encryption Example for encryption instructions.

Request Example (Apple Pay)

{
  "reference": "ORDER-NT-001",
  "terminal_id": "TERM001",
  "description": "Apple Pay payment",
  "currency": "EUR",
  "amount": 10000,
  "transaction_type": "PURCHASE",
  "payment_method": {
    "type": "network-token",
    "data": {
      "encrypted_token_number": "base64_encrypted_dpan",
      "encrypted_expiration_month": "base64_encrypted_value",
      "encrypted_expiration_year": "base64_encrypted_value",
      "encrypted_cryptogram": "base64_encrypted_cryptogram",
      "eci": "05",
      "source": "apple-pay"
    }
  },
  "customer": {
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com",
    "phone": "+1234567890",
    "address": "123 Example Street",
    "city": "Sampletown",
    "country": "BA",
    "postal_code": "12345"
  },
  "browser_info": {
    "user_agent": "Mozilla/5.0...",
    "accept_header": "text/html,application/xhtml+xml...",
    "java_enabled": false,
    "color_depth": 24,
    "screen_height": 1080,
    "screen_width": 1920,
    "time_zone_offset": -120,
    "language": "en-US"
  },
  "metadata": {},
  "return_url": "https://merchant.example.com/return"
}

Request Example (Google Pay — PAN_ONLY)

When using Google Pay with PAN_ONLY authentication, the cryptogram and ECI are not present:

{
  "reference": "ORDER-NT-002",
  "terminal_id": "TERM001",
  "description": "Google Pay payment",
  "currency": "EUR",
  "amount": 5000,
  "transaction_type": "PURCHASE",
  "payment_method": {
    "type": "network-token",
    "data": {
      "encrypted_token_number": "base64_encrypted_dpan",
      "encrypted_expiration_month": "base64_encrypted_value",
      "encrypted_expiration_year": "base64_encrypted_value",
      "source": "google-pay"
    }
  },
  "customer": { ... },
  "browser_info": { ... },
  "metadata": {},
  "return_url": "https://merchant.example.com/return"
}

Network Token Payment Data Fields

Field Type Required Encrypted Description
encrypted_token_number string Yes Yes DPAN from the wallet or TSP
encrypted_expiration_month string Yes Yes Token expiration month (1–12, no leading zero)
encrypted_expiration_year string Yes Yes Token expiration year — 2-digit (26) or 4-digit (2026); must be current or future year
encrypted_cryptogram string No Yes Payment cryptogram (absent for PAN_ONLY)
eci string No No Electronic Commerce Indicator — required for CRYPTOGRAM_3DS, omitted for PAN_ONLY. Valid values: 00, 01, 02, 05, 06, 07
source string No No Token origin: apple-pay, google-pay, or network-token. Defaults to network-token if omitted

For detailed information about network tokens, see the Network Token Reference.


Creating a Customer

Card tokenization associates a saved card with a customer, so the customer must exist before you save a card against it (see Saving Cards for Future Payments). Create one with the Customers API and reference the returned id as customer.id in your transactions.

Create a Customer

POST /api/customers

Request Example:

{
  "email": "john.doe@example.com",
  "first_name": "John",
  "last_name": "Doe",
  "phone": "+38970123456",
  "description": "Repeat customer",
  "address": {
    "country": "SI",
    "address": "123 Main St",
    "city": "Ljubljana",
    "postal_code": "1000",
    "region": "Osrednjeslovenska"
  }
}

Request Fields:

Field Type Required Description
email string Yes Valid email address (max 512). The only required field.
first_name string No Max 255.
last_name string No Max 255.
phone string No Contact phone number.
description string No Free-text label (max 255).
type string No Customer type.
address object No Nested address object (fields below).
address.country string No ISO 3166-1 alpha-2 country code (2 letters).
address.address string No Street address (max 255).
address.city string No Max 255.
address.postal_code string No Max 255.
address.region string No State/region (max 255).

Response Example:

{
  "id": "CUST123456",
  "first_name": "John",
  "last_name": "Doe",
  "email": "john.doe@example.com",
  "phone": "+38970123456",
  "address": "123 Main St",
  "city": "Ljubljana",
  "country": "SI",
  "postal_code": "1000"
}

The returned id is the customer identifier — pass it as customer.id in subsequent authorization/purchase requests to link saved cards to this customer. The response flattens the address into top-level address, city, country, and postal_code.


Saving Cards for Future Payments

The API supports tokenization, allowing you to save payment cards for future transactions. This is useful for:

  • Subscription or recurring payments
  • One-click checkout experiences
  • Repeat customer transactions

Requirements for Card Tokenization

Before using tokenization, ensure:

  1. Tokenization is enabled at the merchant level (contact your payment platform administrator)
  2. Customer exists (if using customer.id): The customer must already exist in the Payment Platform — see Creating a Customer
  3. PCI compliance: Ensure your integration complies with PCI-DSS requirements

Saving a Card During Payment

To save a card for future use, include the tokenization object in your transaction request:

Request Example:

{
  "reference": "ORDER-123456",
  "terminal_id": "TERM001",
  "description": "Order payment",
  "currency": "EUR",
  "amount": 10000,
  "transaction_type": "PURCHASE",
  "payment_method": {
    "type": "card",
    "data": {
      "encrypted_card_number": "base64_encrypted_value",
      "encrypted_cvv": "base64_encrypted_value",
      "encrypted_expiration_month": "base64_encrypted_value",
      "encrypted_expiration_year": "base64_encrypted_value"
    }
  },
  "customer": {
    "id": "CUST123456",
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com",
    "phone": "+1234567890",
    "address": "123 Example Street",
    "city": "Sampletown",
    "country": "BA",
    "postal_code": "12345"
  },
  "tokenization": {
    "save_card_for_future_payments": true
  },
  "browser_info": {
    "user_agent": "Mozilla/5.0...",
    "accept_header": "text/html,application/xhtml+xml...",
    "java_enabled": false,
    "color_depth": 24,
    "screen_height": 1080,
    "screen_width": 1920,
    "time_zone_offset": -120,
    "language": "en-US"
  },
  "metadata": {},
  "return_url": "https://merchant.example.com/return"
}

Tokenization Parameters

Field Type Description
tokenization.save_card_for_future_payments boolean If true, automatically saves the card for future use
tokenization.tokenize_for string Optional. Intended use of the saved payment method: CUSTOMER_INITIATED_TRANSACTION (default when omitted) or MERCHANT_INITIATED_TRANSACTION

Compliance Note: When saving cards without explicit customer consent, ensure your terms of service and privacy policy inform customers that payment information will be stored for future use. Depending on your jurisdiction, this may have regulatory implications (GDPR, PCI-DSS, etc.).

Response with Saved Payment Method

When a card is successfully saved, the response includes a token in the payment_method object:

{
  "result": {
    "id": "TXN123456",
    "status": "APPROVED",
    "payment_method": {
      "method": "411111-******-1111",
      "type": "card",
      "brand": "VISA",
      "masked": "411111-******-1111",
      "token": "abc123def456ghi789"
    },
    ...
  }
}

The token value (e.g., abc123def456ghi789) is the payment method ID that can be used for future transactions.

Retrieving the Token via Webhook

The synchronous response above returns the token in payment_method.token. If you rely on webhooks (for example, hosted-page or redirect flows), the token is delivered on a dedicated event — not on the transaction webhook:

Event: tokenization-webhook:payment-method:tokenized

{
  "event": "tokenization-webhook:payment-method:tokenized",
  "data": {
    "payment_method_id": "abc123def456ghi789",
    "payment_method_type": "CARD",
    "card_brand": "VISA",
    "card_bin": "411111",
    "card_last_4": "1111",
    "card_masked": "411111******1111",
    "card_exp_month": 12,
    "card_exp_year": 2028,
    "customer_id": "CUST123456",
    "transaction_id": "TXN123456"
  }
}

Important: payment_method_token is null on the transaction-svc:transaction:purchase:approved webhook — this is expected, not an error. The token is delivered only on the tokenization-webhook:payment-method:tokenized event. To receive it, your webhook subscription must include that event (webhook subscriptions are configured by Finrelay — contact support to enable it).


Assigning Saved Payment Methods to a Customer

When saving a card, the customer.id field determines how the payment method is associated:

When you include customer.id in the request:

  • The saved payment method is automatically assigned to the specified customer
  • The customer must already exist in the Payment Platform
  • The payment method can be retrieved and used in future transactions for this customer
  • You can list all saved payment methods for a customer

Example:

{
  "reference": "ORDER-123456",
  "customer": {
    "id": "CUST123456",
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com"
  },
  "tokenization": {
    "save_card_for_future_payments": true
  },
  ...
}

Without customer.id

When you do not include customer.id in the request:

  • The payment method is still saved and tokenized
  • The payment method remains unassociated with any customer
  • You can still use the payment method token for future transactions
  • The payment method cannot be associated with a customer later
  • You cannot retrieve this payment method through customer-based queries

Example:

{
  "reference": "ORDER-123456",
  "customer": {
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com"
  },
  "tokenization": {
    "save_card_for_future_payments": true
  },
  ...
}

Important Notes:

  • Payment methods saved with a customer.id are linked to that customer profile and can be managed through customer-based operations
  • Payment methods saved without a customer.id remain permanently unassociated and can only be referenced by their token
  • Best practice: Always include customer.id when saving cards to enable better customer management and payment method organization

Paying with Saved Cards

Once a card has been saved, you can use it for future transactions without requiring the customer to re-enter their card details.

Requirements

To pay with a saved card, you need:

  1. The payment method ID (token) from the original transaction response
  2. The CVV (encrypted) - may be required depending on Payment Provider support

Request Example

{
  "reference": "ORDER-789012",
  "terminal_id": "TERM001",
  "description": "Subscription renewal",
  "currency": "EUR",
  "amount": 5000,
  "transaction_type": "PURCHASE",
  "payment_method": {
    "type": "saved_card",
    "data": {
      "payment_method_id": "abc123def456ghi789",
      "encrypted_cvv": "base64_encrypted_value"
    }
  },
  "customer": {
    "id": "CUST123456",
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com"
  },
  "browser_info": {
    "user_agent": "Mozilla/5.0...",
    "accept_header": "text/html,application/xhtml+xml...",
    "java_enabled": false,
    "color_depth": 24,
    "screen_height": 1080,
    "screen_width": 1920,
    "time_zone_offset": -120,
    "language": "en-US"
  },
  "metadata": {},
  "return_url": "https://merchant.example.com/return"
}

Key Differences for Saved Card Payments

Field Value Description
payment_method.type "saved_card" Indicates using a saved payment method
payment_method.data.payment_method_id string The token from the original save operation
payment_method.data.encrypted_cvv string Base64-encoded encrypted CVV

Security Note:

  • CVV may be required for saved card transactions depending on Payment Provider support
  • When required, CVV must be encrypted using your merchant public key
  • The full card number is never required for saved card payments
  • Only the payment method ID (and CVV if required) are needed

Response Example

{
  "result": {
    "id": "TXN789012",
    "status": "APPROVED",
    "payment_method": {
      "method": "411111-******-1111",
      "type": "saved_card",
      "brand": "VISA",
      "masked": "411111-******-1111",
      "token": "abc123def456ghi789"
    },
    "approved": true,
    ...
  }
}

Deleting a Saved Card

A stored card can be removed at any time — when the customer asks for it, when they remove it themselves from a "saved cards" screen in your application, or when you retire it on your own.

Endpoint

DELETE /api/payment-methods/{payment_method_id}

The {payment_method_id} is the same token returned when the card was saved (the payment_method.token field of the transaction response, or payment_method_id on the tokenization-webhook:payment-method:tokenized event).

The request takes the same OAuth access token as the rest of the API, no request body, and — unlike saving a card — no Idempotency-Key header, because the call is already idempotent.

Your API client needs the payment_method:detach permission. If the call returns 403, contact support to have it enabled for your client.

Request Example

curl -X DELETE "{API_BASE_URL}/api/payment-methods/abc123def456ghi789" \
  -H "Authorization: Bearer <access_token>"

See Environments for the API URL of your environment.

Response Example

200 OK, returning the payment method in its deleted state:

{
  "id": "abc123def456ghi789",
  "type": "CARD",
  "status": "DELETED",
  "customer_id": "",
  "email": "john.doe@example.com",
  "metadata": {},
  "card": {
    "card_brand": "VISA",
    "exp_month": 12,
    "exp_year": 2030,
    "last_4": "1111",
    "bin": "411111"
  },
  "created_at": "2026-01-15T10:30:00Z",
  "updated_at": "2026-02-01T09:12:44Z"
}

What Deletion Does

  • The card can no longer be charged. Any transaction sent with payment_method.type: "saved_card" referencing a deleted payment method is rejected — see Charging a Deleted Card.
  • It is removed from the customer. The customer association is cleared, so the card no longer appears among the saved payment methods offered for that customer.
  • The record is kept. GET /api/payment-methods/{payment_method_id} keeps returning the payment method with status: "DELETED", so a card referenced by an older transaction can still be identified for reconciliation, disputes and audit.
  • Deletion is final. A deleted payment method cannot be reactivated or assigned to a customer again. To pay with the same card later, the customer has to enter it again and it is saved as a new payment method with a new id.

The stored card data itself remains in the vault, encrypted, for as long as dispute handling and regulatory obligations require — it is simply no longer reachable for payments. If a customer requires the card data to be erased permanently (for example a GDPR erasure request), contact support: that is handled as a separate operation.

Repeated Deletion

Deleting is idempotent. Repeating the call for a payment method that is already deleted returns the same 200 OK response and leaves the original deletion untouched, so a delete that timed out can safely be retried.

Unknown or Foreign Payment Method

A payment method id that does not exist — or that belongs to a different merchant — is reported as not found rather than as forbidden, so the response never reveals whether the id exists:

{
  "status": "INVALID",
  "errors": [
    {
      "message": "Not found"
    }
  ]
}

Response Codes

Status Meaning
200 The payment method is deleted (or was already deleted)
403 The API client does not hold the payment_method:detach permission
422 Not found — unknown id, or a payment method belonging to another merchant

Implementing "Remove Card" in Your Application

The call uses your merchant API credentials, so it must be made from your backend. A "remove card" action in a mobile or web application should call your own backend, which then calls the Payment Gateway:

  1. The customer taps Remove card in your application.
  2. Your application calls your backend with the payment method id it is displaying.
  3. Your backend verifies that the payment method belongs to the signed-in customer, then calls DELETE /api/payment-methods/{payment_method_id}.
  4. On 200, your backend removes the card from the list it shows the customer.

Never perform this call directly from a client application: it would require your merchant API credentials to be present in the application. The publishable client key used by Embedded Fields and the Hosted Payment Page cannot delete payment methods.


Error Handling

Tokenization Not Enabled

If tokenization is not enabled at the merchant level:

{
  "status": "INVALID",
  "errors": [
    {
      "property": "tokenization",
      "message": "one of requested features not supported=[TOKENIZATION]"
    }
  ]
}

Solution: Contact your payment platform administrator to enable tokenization.

Customer Not Found

If you provide a customer.id that doesn't exist:

{
  "status": "INVALID",
  "errors": [
    {
      "property": "customer.id",
      "message": "Customer with id 'CUST123456' not found"
    }
  ]
}

Solution: Create the customer first, or omit customer.id to save the payment method without customer association.

Invalid Payment Method ID

If the payment method ID is invalid or doesn't exist:

{
  "status": "INVALID",
  "errors": [
    {
      "property": "payment_method.data.payment_method_id",
      "message": "Payment method not found"
    }
  ]
}

Solution: Verify the payment method ID is correct and that the payment method hasn't been deleted.

Charging a Deleted Card

If a transaction references a payment method that has been deleted (or otherwise deactivated), the transaction is rejected before it reaches the payment provider:

{
  "status": "INVALID",
  "errors": [
    {
      "property": "payment_method.data.payment_method_id",
      "message": "INVALID_REQUEST_PAYMENT_METHOD_INACTIVE"
    }
  ]
}

Solution: The card is no longer available for payments and cannot be restored. Ask the customer to enter the card again, which saves it as a new payment method. You can confirm the state of a payment method with GET /api/payment-methods/{payment_method_id} — a deleted one reports status: "DELETED".


Complete Workflow Examples

Workflow 1: First-Time Payment with Card Saving

  1. Encrypt card data using merchant public key
  2. Submit transaction with tokenization.save_card_for_future_payments: true
  3. Store payment method ID from response for future use
  4. Use saved card for subsequent payments

Workflow 2: Subscription Payment with Saved Card

  1. Retrieve payment method ID from your database (saved from first transaction)
  2. Encrypt CVV (collected from customer)
  3. Submit transaction with payment_method.type: "saved_card"
  4. Process response as normal transaction

Workflow 3: Customer Removes a Saved Card

  1. Customer requests removal of a card in your application
  2. Verify ownership in your backend — the payment method must belong to the signed-in customer
  3. Call DELETE /api/payment-methods/{payment_method_id} with your merchant API credentials
  4. Remove the card from the list shown to the customer on 200; the payment method stays retrievable with status: "DELETED" for your own reconciliation


Best Practices

  1. Always encrypt sensitive data: Card numbers, CVV, and expiration dates must be encrypted
  2. Use customer IDs: Always associate saved cards with a customer ID for better organization
  3. Handle tokens securely: Store payment method IDs securely in your database
  4. Implement webhooks: Use webhooks for reliable payment status updates
  5. Validate responses: Always check the status and approved fields in responses
  6. Error handling: Implement comprehensive error handling for all API calls
  7. Compliance: Ensure your integration meets PCI-DSS and local regulatory requirements

Go further

Save cards for future payments

Let customers opt in, or always save the card.

Read guide

3DS card payment

A complete recipe with challenge flow.

Open recipe

Webhooks

Get notified when a payment changes status.

View reference