hup Developer Api documentation

Download OpenAPI specification:Download

Hi 👋,

Before you skip straight to the code please take a sip of your brew and read this block.

  • HUP provides realtime developer to developer support for integration partners, join our Telegram group at this link and ask away.
  • We are always open for suggestions or improvements
  • Invalid requests might result in a security block. API suddenly refusing connection? Contact us.

About HUP

HUP is a platform that connects payment methods to Payment Service Providers (PSPs) and enables offering proprietary payment methods within the Buy Now Pay Later (BNPL) ecosystem.

Glossary

  • Client: Payment Service Provider (PSP)
  • Customer: The user of the Shop that wants to purchase a service/good
  • Merchant: Customer of the Client which owns shops or points of sale. The entity that is billed for HUP's services
  • Shop: Point of sale that belongs to a Merchant

Contact

If you have any questions regarding the integration with our Api, please contact one of the following:

Development:

Merchant/Shop/Customer:

Important

Before starting integration with HUP, check if all legal documents (KYC checks) and merchant contracting are covered in your onboarding!

Transaction flow

Basic flow

sequenceDiagram participant Customer participant Merchant participant PSP participant Hup participant BNPL

Customer->>Merchant: Start checkout
Merchant->>PSP: Create order with details
PSP->>Hup: Forward transaction request
Hup->>BNPL: [POST] Start transaction
BNPL-->>Hup: Payment screen URL
Hup-->>PSP: Pass back payment screen URL
PSP-->>Merchant: Redirect customer
Merchant-->>Customer: Show BNPL payment screen

Customer->>BNPL: Authenticate & pay first installment
BNPL-->>Hup: Payment status update
Hup-->>PSP: Payment status
PSP-->>Merchant: Success / Failure
Merchant-->>Customer: Show order result

Transactions

Start a new transaction

Transaction start

Starts an in3 transaction.

Note: At in3 we always prefer that you provide array of invoiceLines in your Api call, if it`s not possible to provide the invoiceLines, we can work around it.

Please contact us on: product@payin3.nl

Authorizations:
API Key
Request Body schema: application/json
required
object (CustomerInfo)
object (InvoiceInfo)
required
Address (object)

Address where the products will be shipped

(Address (object or null))

Address where the invoice will be sent, same as shipping address when null

Array of objects (InvoiceLine)
required
object (ApiOptions)
(Psper (object or null))

PSPer only! Used by PSPers to facilitate in3 transactions for connected shops

Responses

Callbacks

Request samples

Content type
application/json
{
  • "customerInfo": {
    },
  • "invoiceInfo": {
    },
  • "shippingAddress": {
    },
  • "invoiceAddress": {
    },
  • "invoiceLines": [
    ],
  • "apiOptions": {},
  • "pspOptions": {
    }
}

Response samples

Content type
application/json
{}

Transaction status

Gets the status of an in3 transaction.

Status descriptions:

  • 'New' - The transaction was created but the customer did not yet open the in3 payment screen.
  • 'InProgress' - The customer has arrived at the in3 payment screen.
  • 'Rejected' - The customer was not allowed to complete the checkout.
  • 'FirstTermPaid' - The customer paid the first term and the transaction should be processed.
  • 'Cancelled' - The customer voluntarily aborted the transaction.
  • 'Expired' - The payment expired, expiration is provided by the PSPer. If not set on transaction creation the transaction will never expire.

The following state transitions are possible. Note that only the flow that ends in FirstTermPaid is considered a succesful flow. In all other cases, the payment did not go through and no goods should be shipped by the merchant.

State Transitions

Authorizations:
API Key
path Parameters
transactionIdentifier
required
string[a-zA-Z0-9\-]+

/api/transaction/{transactionIdentifier} (Returned by POST /api/transaction)

Responses

Response samples

Content type
application/json
{
  • "status": "FirstTermPaid"
}

Transaction list refunds

List all credits/restitutions/refunds for the specified transaction.

Authorizations:
API Key
path Parameters
transactionIdentifier
required
string[a-zA-Z0-9\-]+

/api/transaction/{transactionIdentifier} (Returned by POST /api/transaction)

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Transaction refund

Creates a credit/restitution/refund for the specified order. Multiple refunds are allowed, but the sum of all refunds may not exceed the original invoice amount.

Authorizations:
API Key
path Parameters
transactionIdentifier
required
string[a-zA-Z0-9\-]+

/api/transaction/{transactionIdentifier} (Returned by POST /api/transaction)

Request Body schema: application/json
description
required
string or null <= 256 characters

The description of the refund. E.g. return number

amount
required
number >= 1

The amount that should be refunded in cents. Cannot be higher than the original invoice amount

Responses

Request samples

Content type
application/json
{
  • "description": "2f5c43a5-570b-4206-b5fd-882ea51eb8c3",
  • "amount": 24234
}

Response samples

Content type
application/json
{
  • "identifier": "2Upb15Afx364f064d26Js26F80b73Q4d27b3cxy5fmEbCdbl"
}

Capture

Total captured amount across all capture requests must not exceed the originally authorized transaction amount.
Multiple partial captures are allowed.
Transactions without capture after a defined window (e.g., 30 days) may expire automatically.

Get Capture information

Authorizations:
API Key
path Parameters
transactionIdentifier
required
string[a-zA-Z0-9\-]+

/api/transaction/{transactionIdentifier} (Returned by POST /api/transaction)

Responses

Response samples

Content type
application/json
{
  • "transactionIdentifier": "0af7c4b50f894420bc1ec565974bbf64fWBHL2tpvbhs3WQoN2MlPlR",
  • "totalAuthorized": 5000,
  • "totalCaptured": 5000,
  • "remaining": 5000,
  • "captures": [
    ]
}

Post Capture information

Total captured amount across all capture requests must not exceed the originally authorized transaction amount.
Multiple partial captures are allowed.
Transactions without capture after a defined window (e.g., 30 days) may expire automatically.

Authorizations:
API Key
path Parameters
transactionIdentifier
required
string[a-zA-Z0-9\-]+

/api/transaction/{transactionIdentifier} (Returned by POST /api/transaction)

Request Body schema: application/json
amount
required
number >= 1

The amount in cents that was affected in this capture event

currency
required
string <= 3 characters

The currency in ISO 4217

captureReference
required
string <= 256 characters

The reference for this capture event

Responses

Request samples

Content type
application/json
{
  • "amount": 5000,
  • "currency": "EUR",
  • "captureReference": "SHIPMENT-001"
}

Response samples

Content type
application/json
{
  • "transactionIdentifier": "0af7c4b50f894420bc1ec565974bbf64fWBHL2tpvbhs3WQoN2MlPlR",
  • "capturedAmount": 5000,
  • "currency": "EUR",
  • "captureReference": "SHIPMENT-001",
  • "status": null,
  • "remaining": 5000,
  • "timestamp": "2025-04-11T09:10:35Z"
}

Extend Capture Time

Extends the capture window for transaction.

Authorizations:
API Key
path Parameters
transactionIdentifier
required
string[a-zA-Z0-9\-]+

/api/transaction/{transactionIdentifier} (Returned by POST /api/transaction)

Request Body schema: application/json
newExpiryDate
required
string >= 1

The new deadline date in ISO 8601 in UTC time

reason
required
string <= 1024 characters

The reason for extending the capture window

Responses

Request samples

Content type
application/json
{
  • "newExpiryDate": "2025-04-11T09:10:35Z",
  • "reason": "Supply Issues"
}

Response samples

Content type
application/json
{
  • "transactionIdentifier": "0af7c4b50f894420bc1ec565974bbf64fWBHL2tpvbhs3WQoN2MlPlR",
  • "oldExpiryDate": "2025-04-11T09:10:35Z",
  • "newExpiryDate": "2025-04-14T09:10:35Z"
}

Webhook

Get informed when something relevant changes. Here you can setup which URL we should inform. All webhooks only inform you of the changed ID's so that you can fetch the latest status yourself completing the triangle.

Confirming HMAC on the receiving side of the webhook is as following:

  • Combine the full response body with the unix timestamp from the response header: "hmac_date", and seperate them with a semicolon. {"key":"value"};1646147678
  • Encrypt the following value fully with SHA512, using the signing key from the creation request.
  • If the result hash equals the hmac from the response header, you have a valid HMAC.
/// <summary>
/// Verifies HMAC from webhook
/// </summary>
/// <param name="hmac">Hmac header</param>
/// <param name="hmacDate">Hmac_date header</param>
/// <param name="jsonBody">The full response body</param>
/// <param name="signingKey">The signing key that was transmitted on the creation of the webhook.</param>
/// <returns></returns>
public bool VerifyHmac(string hmac, long hmacDate, string jsonBody, string signingKey)
{
    if (DateTimeOffset.FromUnixTimeSeconds(hmacDate) > DateTimeOffset.Now.AddMinutes(15))
    {
        // Timestamp in HMAC is too old, or modified.
        return false;
    };

    var payload = $"{jsonBody};{hmacDate}";
    
    var signingKeyDecoded = Convert.FromBase64String(signingKey);

    var signature = string.Empty;
    using (var hmacsha512 = new HMACSHA512(signingKeyDecoded))
    {
        var stream = new MemoryStream(Encoding.UTF8.GetBytes(payload));
        foreach (var b in hmacsha512.ComputeHash(stream))
        {
            signature = signature + $"{b:x2}";
        }
    }

    return hmac == signature;
}

List webhooks

Authorizations:
API Key

Responses

Request samples

GET /api/v3/webhook HTTP/1.1
Host: webhook.partner.eu
x-hmac: b8b6fa39866b125555772e955107a641522a610091dcdc734c7a8d1ea1f92563f060e8ebb9263f7b5a3173191594320b6d50c7cc19d76be42e2df3ed1d3e4c20
x-hmac-date: 1656418731

{"test":"test"}

Signing_Key: PRHVXZL739Hu8kVaZyxzMUGGMe/w12Meuy9aRQo7BFxf7oYoepN/GsY3ZmCotsuJtoxSfYKpEEjsyvrAUWDyzA==

Response samples

Content type
application/json
[
  • {
    }
]

Create a webhook

Authorizations:
API Key
Request Body schema: application/json
name
required
string <= 128 characters

The name of the webhook

url
required
string <= 2048 characters

The URL in3 will call

eventType
required
string
Enum: "TransactionState" "OnboardingState" "FraudState" "TransactionCaptureState"

The type of events this webhook should receive

expectedResponseMessage
string or null <= 25 characters
Default: null

The response body we should expect to consider the callback successful. Empty is always OK

expectedStatusCode
number or null
Default: 200

The response body we should expect to consider the callback successful.

retryPolicy
string
Default: "NoRetry"
Enum: "NoRetry" "Retry"

With the retry policy we will retry the call a few more times with increasing interval

Responses

Callbacks

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "id": "49e2576a-8cab-4ded-99fc-36feb779e69e",
  • "name": "Failover event ingest - in3 onboarding",
  • "eventType": "OnboardingState",
  • "expectedResponseMessage": "ACK",
  • "expectedStatusCode": 200,
  • "retryPolicy": "Retry",
  • "signingKey": "FX0369C0DN0402H3DB0SIJSLW0E2HAQ4"
}

Callback payload samples

Callback
POST: Definition of the outbound call
Content type
application/json
{
  • "id": 294241568,
  • "event": "OnboardingState",
  • "entityId": "49e2576a-8cab-4ded-99fc-36feb779e69e"
}

Delete a webhook

Authorizations:
API Key
path Parameters
webhookId
required
string[a-zA-Z0-9\-]+

/api/webhook/{webhookId}

Responses

Request samples

DELETE /api/v3/webhook/521a61c8-ba9d-4c16-95f3-08d9c3024e15 HTTP/1.1
Host: webhook.partner.eu
x-hmac: b8b6fa39866b125555772e955107a641522a610091dcdc734c7a8d1ea1f92563f060e8ebb9263f7b5a3173191594320b6d50c7cc19d76be42e2df3ed1d3e4c20
x-hmac-date: 1656418731

{"test":"test"}

Signing_Key: PRHVXZL739Hu8kVaZyxzMUGGMe/w12Meuy9aRQo7BFxf7oYoepN/GsY3ZmCotsuJtoxSfYKpEEjsyvrAUWDyzA==