OP Corporate Refund API (2.0.2)

Download OpenAPI specification:Download


OP Corporate Refund API allows OP corporate customers to refund a SEPA credit transaction in full or partially.

The API has two versions with a difference between them being in the archiveId parameter:

  • V1 allows the use of OP originated archive IDs with a format of 16, 18 or 20 characters as the archiveId parameter.
  • V2 allows the use of different archive ID formats by using the SEPA filing code in their full length as the archiveId parameter.

We recommend using V2 for archive ID compatibility reasons.

Authentication

Authentication in OP Corporate Refund API is based on practices from OpenID Connect (OIDC) 1.0 and OAuth 2.0.

Security Considerations

It is extremely important that the client_id, client_secret and client certificate private key are not exposed at any point. Together they represent the identity of the client and thereby the corporation. In the possession of an attacker they could be used to make fraudulent API calls potentially causing considerable damage.

Sandbox

To test the API in the sandbox environment, please send an email at corp-payment-APIs@op.fi. You will be granted a user ID and password to access OP API Admin in the sandbox environment.

In general, mock data is used for sandbox testing and we have provided for the following test scenarios:

  • A successful response (201) can be received by entering a valid archive ID for an account coupled with the sandbox agreement. In the sandbox the archive ID needs to be 20 characters long and start with a date followed by a number 5. Example archive IDs: 20220616567890123456 and 20200810598712345678.
  • An error response (400) with message "Already refunded" will be returned if a payment is refunded twice a day.
  • An error response (403) with message "Not authorized to refund" will be returned for refunds where the entered account is not part of the sandbox agreement.
  • An error response (404) with the message "Transaction not found" will be returned if an invalid archive ID format (e.g. 12345678901234567890) is entered, for example.

Production access

You can get production access for this API on OP API Admin in production. To use these APIs in production you should replace host in the API examples below with https://api.corporate-api.op.fi/

Refund request signature

To ensure that the refund request payload is not altered and evidence of non-repudiation is maintained, a signature calculated over the refund request body. The signature is a detached content JWS, as defined in RFC7515 with the following mandatory JWS headers:

  • alg - string - only RS256 algorithm is currently supported for the signature algorithm
  • kid - string - The Signature Key Identifier (KID) received from the registration ui, used to map the signing key to the correct public key at the verifying end.
  • b64 - boolean - This must have the boolean value false. This indicates that the detached message payload is not base64 URL encoded when calculating the request signature. See RFC 7797 - The "b64" header Parameter
  • urn:op.api.iat - number - This must be a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in GMT until the date/time.
  • crit - array of string - This must be an array containing the critical header names, ["b64","urn:op.api.iat"]

The signature is computed in the detached mode, where the payload is not base64 URL encoded when forming the signature base string, but the UTF-8 encoded bytes of the sent request body are used as-is to construct the signature base string. See the example code below for more details.

When sending the refund request, the signature must be included in the request header "X-Req-Signature", and it will be validated against the key matching the kid parameter.

Change log

v2.0.2 (03/2024)

Changed

  • Changed sandbox and production URLs for APIs and OP API Admin

v2.0.1 (01/2024)

Added

v2.0 (01/2023)

Added

  • New endpoint Refund V2
    • Improved request parameter archiveId handling and a new request parameter transactionDate for entering the date when the original transaction was received. See documentation below for details.
  • New request parameter endToEndId added to endpoint Refund V1 in order to help users identify refund transactions

v1.3 (09/2022)

Changed

  • Error messages adjusted to be more informative
  • Documentation overview and response parameter paymentType descriptions fine tuned
  • Production server address added to section Production access

v1.2 (02/2022)

Added

  • Support for 16 and 18 character archive IDs. See request parameter archiveId below for details

v1.1 (09/2021)

Added

  • Refund API now supports service provider usage

v1.0 (04/2020)

Initial version

Usage example

#!/bin/bash

# To run this you need openssl and jq installed.

# Steps for registering the required keys and certificates
# 1. Valid Corporate API contract created through the web UI
# 2. OAuth clientId and clientSecret provisioned
# 3. MTLS private key generated: openssl genrsa -out sandbox-mtls.key 4096
# 4. MTLS certificate signing request (CN and other attributes are ignored): openssl req -new -key sandbox-mtls.key -out sandbox-mtls.csr
# 5. Valid MTLS certificate acquired from the web UI using the CSR from step 4. and in "sandbox-mtls.crt" file
# 6. Payment signing private key generated: openssl genrsa -out sandbox-signing.key 4096
# 7. Signing CSR request: openssl req -new -key sandbox-signing.key -out sandbox-signing.csr
# 8. Signing KID (key identifier) received from the web UI by submitting the sandbox-signing.csr.

# OAuth credentials
clientId="TODO put here oauth client id"
clientSecret="TODO and here client secret"
# MTLS credentials
mtlsKey="sandbox-mtls.key"
mtlsCertificate="sandbox-mtls.crt"

# Payment signing credentials
signingKey="sandbox-signing.key"
signingKid="TODO and here signing KID"

# Required information about the payment to refund
REFUND_ACCOUNT="account iban"
REFUND_ARCHIVE_ID="archiveId of the credit transaction to refund"

API_SERVER="https://api.corp-api-sandbox.test.aws.op-palvelut.net"

# Get access token
echo "Getting access token"
reply=$(curl -s ${API_SERVER}/corporate-oidc/v1/token \
    --key ${mtlsKey} \
    --cert ${mtlsCertificate} \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d "grant_type=client_credentials&client_id=${clientId}&client_secret=${clientSecret}")

token=$(echo $reply | jq -r .access_token)
echo "Access token is: $token"

# Sample refund request with parametrized values
refundRequest="{\"accountIban\":\"$REFUND_ACCOUNT\",\"archiveId\":\"$REFUND_ARCHIVE_ID\"}"

## Signing the payload is mandatory for refunds

# Issued at, in unix timestamp format
IAT=$(date +%s)

# Form the JWS header with mandatory fields
HEADER="{\"b64\":false,\"crit\":[\"b64\",\"urn:op.api.iat\"],\"alg\":\"RS256\",\"urn:op.api.iat\":${IAT},\"kid\":\"${signingKid}\"}"

# Base64 encode the header. JWT encoding is slightly different from default output of base64, so do some transforming to make the output url-safe, and remove trailing padding (=).
HEADER_ENC=$(echo -n "${HEADER}" | base64 | tr -- '+/=' '-_ ' | tr -d '[:space:]')

# Sign the header and payload. The signature calculation in the detached body JWS is slightly different from the standard way, as the payload is not base64-encoded
# before signing, but included as-is in the signature plaintext. The signing algorithm is RS256, also known as "RSA Digital Signature Algorithm with SHA-256".
# The resulting signature is base64-encoded, and the same url-safe transformations are applied.
SIGNATURE=$(echo -n "${HEADER_ENC}.${refundRequest}" | openssl dgst -sha256 -sign ${signingKey} -binary | openssl base64 -e -A | tr -- '+/=' '-_ ' | tr -d '[:space:]')

# The signature header includes the base64-encoded JWS header, followed by two dots (..), and the base64-urlsafe-encoded signature. The actual message payload is not
# repeated in the signature
REQ_SIGNATURE="${HEADER_ENC}..${SIGNATURE}"

# The signature is included in the "X-Req-Signature" header.
echo "Sending refund request"
paymentResponse=$(curl -s ${API_SERVER}/corporate-payment/v1/payment-refund \
    --key ${mtlsKey} \
    --cert ${mtlsCertificate} \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $token" \
    -H "X-Req-Signature: ${REQ_SIGNATURE}" \
    -d "$refundRequest")

echo $paymentResponse | jq -C .

Refund V1

Refunds credit transaction in full or partial amount.

header Parameters
authorization
required
string

Bearer JWT token.

X-Req-Signature
required
string

Body signature.

X-Request-ID
string

Unique identifier for a specific request. Used for debugging purposes.

Request Body schema: application/json; charset=UTF-8
required

Refund details.

accountIban
required
string

Account number in IBAN format. This is the number of the account which has received the original payment.

archiveId
required
string [ 16 .. 20 ] characters

Archive ID (filing code) of the credit transaction to refund. The archive ID should have 16, 18 or 20 characters, and 12th character from the right should be the number 5.

A transaction with same archive ID cannot be refunded more than once a day.

amount
string^\d{1,13}\.\d{2}$

Amount to refund. Cannot exceed the amount of original transaction. If absent, the original amount will be used.

message
string [ 1 .. 100 ] characters

Free form message included in the auto-generated refund message to the creditor. The message has also a fixed part “MAKSUN PALAUTUS. Maksun tiedot: dd.mm.yyyy”, which is followed by the free form message. (dd.mm.yyyy indicates the date of the original payment.)

endToEndId
string^[0-9A-Za-z-åäöÅÄÖ_=:.,+]{1,35}$

Unique identification, as assigned by the original initiating party, to unambiguously identify the original transaction.

Responses

Request samples

Content type
application/json; charset=UTF-8
{
  • "archiveId": "20190524593156999999",
  • "amount": "12.35",
  • "message": "Less money, fewer problems",
  • "accountIban": "FI4550009420888888",
  • "endToEndId": "544652-end2end"
}

Response samples

Content type
application/json; charset=UTF-8
{
  • "original": {
    },
  • "refund": {
    }
}

Refund V2

Refunds credit transaction in full or partial amount.

header Parameters
authorization
required
string

Bearer JWT token.

X-Req-Signature
required
string

Body signature.

X-Request-ID
string

Unique identifier for a specific request. Used for debugging purposes.

Request Body schema: application/json; charset=UTF-8
required

Refund details.

accountIban
required
string

Account number in IBAN format. This is the number of the account which has received the original payment.

archiveId
required
string [ 16 .. 35 ] characters

Archive ID (filing code) of the credit transaction to refund. The archive ID should be added in full length. Usually, the format with 16 or 18 characters is truncated. In electronic bank statement choose SEPA filing code.

A transaction with same archive ID cannot be refunded more than once a day.

transactionDate
required
string

The date when the transaction was received. Date should be given without a time zone in the ISO-8601 calendar system. The date cannot be in the future or older than 12 months.

amount
string^\d{1,13}\.\d{2}$

Amount to refund. Cannot exceed the amount of original transaction. If absent, the original amount will be used.

message
string [ 1 .. 100 ] characters

Free form message included in the auto-generated refund message to the creditor. The message has also a fixed part “MAKSUN PALAUTUS. Maksun tiedot: dd.mm.yyyy”, which is followed by the free form message. (dd.mm.yyyy indicates the date of the original payment.)

endToEndId
string^[0-9A-Za-z-åäöÅÄÖ_=:.,+]{1,35}$

Unique identification, as assigned by the original initiating party, to unambiguously identify the original transaction.

Responses

Request samples

Content type
application/json; charset=UTF-8
{
  • "archiveId": "20190524593156999999999999999999999",
  • "amount": "12.35",
  • "message": "Less money, fewer problems",
  • "accountIban": "FI4550009420888888",
  • "transactionDate": "2023-01-14T00:00:00.000Z",
  • "endToEndId": "544652-end2end"
}

Response samples

Content type
application/json; charset=UTF-8
{
  • "original": {
    },
  • "refund": {
    }
}