OP Yrityssiirto
Briefly
Welcome to Pivo Payment API Reference for OP Yrityssiirto
OP Yrityssiirto is an API which enables merchants to pay in real time from their OP account to receiver’s bank account.
View the complete Pivo Payment API documentation
Provisioning
Contact Us and we will create You an account.
Terminology
Listed terms we are using in our Payment API.
Term | Explanation |
---|---|
You | The customer who is using the API. |
We | Pivo Payment API. |
Merchant | You |
Examples
Please note that the presented example data is not valid (e.g iban numbers, iban owners and card tokens)
Character encoding
We use UTF-8 in all requests and responses.
REST API - Authorization
Pivo Payment API relies on OAuth2 Client Credentials Grant Authorization.
Subsequent resource API calls require an access token, which can be established with provisioned client credentials.
To use resources You need to request specific access request scope when creating access token (e.g. payments)
Don’t leak Your OAuth client secret to users nor mobile applications.
Getting an Access Token
POST https://qa-maksu-api.pivo.fi/oauth/token
Getting an Access Token with Your credentials.
A successful response contains the access token.
cUrl Example:
$ curl "https://qa-maksu-api.pivo.fi/oauth/token" -d '{"client_id":"payment_api_user","client_secret":"30713f017cf49f1cde8c058446273d02ef040548178a89c050c7aff357729178","scope":"payments acquirings","grant_type":"client_credentials"}' -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json"
Request Header:
Accept: application/json
Content-Type: application/json
Request body:
{
"client_id": "payment_api_user",
"client_secret": "30713f017cf49f1cde8c058446273d02ef040548178a89c050c7aff357729178",
"scope": "payments acquirings",
"grant_type": "client_credentials"
}
Response with status 200:
{
"access_token": "9de58b14e0115eb86e970eea44d7a4368787f132f1a77fd6e2ed4a2c5adeae57",
"token_type": "bearer",
"expires_in": 7200,
"scope": "payments acquirings",
"created_at": 1544611484
}
Query Parameters
Name | Required | Type | Description |
---|---|---|---|
client_id | true | String | The client id |
client_secret | true | String | The client secret |
scope | true | String | The scope of the access request. E.g. ‘payments’ |
grant_type | true | String | Type of the scope. Use ‘client_credentials’ |
Response Fields
Name | Type | Description |
---|---|---|
access_token | String | The access token |
token_type | String | Type of the token |
expires_in | Integer | Expire time of access token in seconds |
scope | String | OAuth scope of the session |
created_at | Integer | Unix timestamp of creation time |
REST API - OP Yrityssiirto
For creating OP Yrityssiirto payments
Please see the section for detailed information when implementing signature calculation.
Dedicated documentation
Dedicated API documentation which only contains Authorization and this section.
Access scope: other_payments
To use this resource You need to request a specific access scope when getting access token.
Typical Error Codes
Error codes 4xx are client errors. On an error, please check Your parameters and access token.
Error Code | Message | Meaning |
---|---|---|
400 | Bad request | Check Your parameters |
401 | Unauthorized | No access token provided or the access token is invalid. Signature is not correct. |
403 | Forbidden | The access token does not have proper scope OR we are missing Your account number in provision data |
412 | Precondition Failed | Check Your parameters again (e.g. format or business_id) |
422 | Unprocessable Entity | Payment unprocessable. User can request more information from payment method provider. |
Create OP Yrityssiirto payment with public key parameter signature
POST https://qa-maksu-api.pivo.fi/other/payments
Response contains archive_number
cUrl Example:
$ curl "https://qa-maksu-api.pivo.fi/other/payments" -d '{"amount":100,"originator":{"iban":"FI5558498520024222","name":"Iban owner name","business_id":"FI66554436","industry_class_code":"4752"},"beneficiary":{"iban":"FI2159986920069366","name":"Virtanen","first_names":"Oskari Olavi"},"ultimate_originator":{"name":"Korhonen Pekka Tuomas","ssn":"240811-1234","business_id":"24","industrial_class_code":"08"},"message":"Message for receiver","reference":"14932116599460307792","signature":"pk:payment_api_user/ae4c6330abae2d13d646895bc397b0030ed378d0dada0a29907c1e5ae83e0731 jOhd62LYpHLGaunHprdeloEIQutUBVqPjUa3ndZr7Fr1/R2Atwxf6Iow7J6BCHVcj4ZLYPNm91Pp/z85FXEpnze+pTcdfP2yThdVrqniAllDZTvWGArsBZVUVJdxM/NUkxkWvYGTrSoOVhAnZ8Q9VbnvJsK+rEymRK7CCThULRQd9BK1gLWfjfpsKIdliOdT3xr/XUDciJGtnimTJtfvlyt24CwzyQ055UGXTL52eciJYNMYlgBPUZictee8QjGp40epF3AFm6OpYch/G7FEc4aMT8D7DB/zTGsttUGvUSBCrUADyde4WrXnSM4ds9siMoDgdOkKV8/90C61MPs9mQ=="}' -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json"
Request Header:
Accept: application/json
Content-Type: application/json
Authorization:
Request body:
{
"amount": 100,
"originator": {
"iban": "FI5558498520024222",
"name": "Iban owner name",
"business_id": "FI66554436",
"industry_class_code": "4752"
},
"beneficiary": {
"iban": "FI2159986920069366",
"name": "Virtanen",
"first_names": "Oskari Olavi"
},
"ultimate_originator": {
"name": "Korhonen Pekka Tuomas",
"ssn": "240811-1234",
"business_id": "24",
"industrial_class_code": "08"
},
"message": "Message for receiver",
"reference": "14932116599460307792",
"signature": "pk:payment_api_user/ae4c6330abae2d13d646895bc397b0030ed378d0dada0a29907c1e5ae83e0731 jOhd62LYpHLGaunHprdeloEIQutUBVqPjUa3ndZr7Fr1/R2Atwxf6Iow7J6BCHVcj4ZLYPNm91Pp/z85FXEpnze+pTcdfP2yThdVrqniAllDZTvWGArsBZVUVJdxM/NUkxkWvYGTrSoOVhAnZ8Q9VbnvJsK+rEymRK7CCThULRQd9BK1gLWfjfpsKIdliOdT3xr/XUDciJGtnimTJtfvlyt24CwzyQ055UGXTL52eciJYNMYlgBPUZictee8QjGp40epF3AFm6OpYch/G7FEc4aMT8D7DB/zTGsttUGvUSBCrUADyde4WrXnSM4ds9siMoDgdOkKV8/90C61MPs9mQ=="
}
Response with status 200:
{
"archive_number": "20180119593064010085",
"status": "paid"
}
Query Parameters
Name | Required | Type | Description |
---|---|---|---|
amount | true | Integer | Amount in euro cents |
originator | true | JSON object | Originator object presenting the payer company |
originator.iban | true | String | The account number in IBAN format |
originator.name | true | String | The account owner name |
originator.business_id | true | String | The business id of the payer company. International format (ALV number). |
originator.industry_class_code | true | String | The industry class code of the payer company |
beneficiary | true | JSON object | Beneficiary object presenting the receiver(payee) |
beneficiary.iban | true | String | The account number in IBAN format |
beneficiary.name | true | String | The account owner name (person last name or company name) |
beneficiary.first_names | false | String | The account owner first names (for person, not required for company) |
ultimate_originator | false | JSON object | Ultimate originator object presenting the party on whose behalf the originator is paying |
ultimate_originator.name | true | String | The name of the ultimate receiver (for person lastname and firstnames or company name) |
ultimate_originator.ssn | false | String | The ssn of the person (required for person) |
ultimate_originator.business_id | false | String | The business_id of the company (required for company). International format (ALV number). |
ultimate_originator.industrial_class_code | false | String | The industrial_class_code of the company (required for company) |
message | false | String | message |
reference | true | String | Payment’s reference number in standard format (viite). If not present ‘message’ is used. |
signature | true | String | Private key calculated signature from other fields. Use dot-notation for nested elements, order alphabetically and calculate private key signature as presented in Signature section |
Response Fields
Name | Type | Description |
---|---|---|
archive_number | String | Archive number of the created payment |
status | String | paid |
Create OP Yrityssiirto payment
POST https://qa-maksu-api.pivo.fi/other/payments
Response contains archive_number
cUrl Example:
$ curl "https://qa-maksu-api.pivo.fi/other/payments" -d '{"amount":100,"originator":{"iban":"FI5558498520024222","name":"Iban owner name","business_id":"FI66554436","industry_class_code":"4752"},"beneficiary":{"iban":"FI2159986920069366","name":"Virtanen","first_names":"Oskari Olavi"},"message":"Message for receiver","reference":"14932116599460307792"}' -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json"
Request Header:
Accept: application/json
Content-Type: application/json
Authorization: Bearer 21a1f772086ddf65fa7020ffc02f95a5294d6059c5f34943eec6edbbf6fb638c
Request body:
{
"amount": 100,
"originator": {
"iban": "FI5558498520024222",
"name": "Iban owner name",
"business_id": "FI66554436",
"industry_class_code": "4752"
},
"beneficiary": {
"iban": "FI2159986920069366",
"name": "Virtanen",
"first_names": "Oskari Olavi"
},
"message": "Message for receiver",
"reference": "14932116599460307792"
}
Response with status 200:
{
"archive_number": "20180119593064010085",
"status": "paid"
}
Query Parameters
Name | Required | Type | Description |
---|---|---|---|
amount | true | Integer | Amount in euro cents |
originator | true | JSON object | Originator object presenting the payer company |
originator.iban | true | String | The account number in IBAN format |
originator.name | true | String | The account owner name |
originator.business_id | true | String | The business id of the payer company. International format (ALV number). |
originator.industry_class_code | true | String | The industry class code of the payer company |
beneficiary | true | JSON object | Beneficiary object presenting the receiver(payee) |
beneficiary.iban | true | String | The account number in IBAN format |
beneficiary.name | true | String | The account owner name (person last name or company name) |
beneficiary.first_names | false | String | The account owner first names (for person, not required for company) |
message | false | String | message |
reference | true | String | Payment’s reference number in standard format (viite). If not present ‘message’ is used. |
Response Fields
Name | Type | Description |
---|---|---|
archive_number | String | Archive number of the created payment |
status | String | paid |
Create OP Yrityssiirto payment with public key header signature
POST https://qa-maksu-api.pivo.fi/other/payments
Request contains Request-Id
and Signature
headers.
Signature is calculated from the payload (body).
⚠ Note the curl example uses wrongly Request
please use Request-Id
as defined!
Response contains archive_number and the status of the payment
cUrl Example:
$ curl "https://qa-maksu-api.pivo.fi/other/payments" -d '{"amount":100,"originator":{"iban":"FI5558498520024222","name":"Iban owner name","business_id":"FI66554436","industry_class_code":"4752"},"beneficiary":{"iban":"FI2159986920069366","name":"Virtanen","first_names":"Oskari Olavi"},"ultimate_originator":{"name":"Korhonen Pekka Tuomas","ssn":"240811-1234","business_id":"24","industrial_class_code":"08"},"message":"Message for receiver","reference":"14932116599460307792"}' -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Request: 8eb15bc6-b6fa-497d-adf4-97f067ea052f" \
-H "Signature: payment_api_user/ae4c6330abae2d13d646895bc397b0030ed378d0dada0a29907c1e5ae83e0731 eUotQ2nhZRok8UNH34z8VSeLpfCWoFIwddxHvwjvttZErQk33rM5rii+nkB7ZRrpwkC/lRXymSHWAzhBThhdYwVB6Hzk77ljmFObebla8hXuaKFcqBMrG0LkkBx83BNiLVDr31VxgYqMYnyd7jluJTjKcfzCsfNssrOHb9BfhZfzD4x1Gysn8nIkkXfm9eeIgASFtHNA+jBQddon9c+D3BrwpOe3zlqz+15tzkhJ1zScrlLVKU7m0fwvmzh9RQrFVeeFKbmrxg/3BZ3j6ybeG0s+vxYFXwXCFjKZiuTfgLyGlffAfgWn5NSze3mexXsRlQw4OX970yfTCfawqyziyQ=="
Request Header:
Accept: application/json
Content-Type: application/json
Authorization:
Request-Id: 8eb15bc6-b6fa-497d-adf4-97f067ea052f
Signature: payment_api_user/ae4c6330abae2d13d646895bc397b0030ed378d0dada0a29907c1e5ae83e0731 eUotQ2nhZRok8UNH34z8VSeLpfCWoFIwddxHvwjvttZErQk33rM5rii+nkB7ZRrpwkC/lRXymSHWAzhBThhdYwVB6Hzk77ljmFObebla8hXuaKFcqBMrG0LkkBx83BNiLVDr31VxgYqMYnyd7jluJTjKcfzCsfNssrOHb9BfhZfzD4x1Gysn8nIkkXfm9eeIgASFtHNA+jBQddon9c+D3BrwpOe3zlqz+15tzkhJ1zScrlLVKU7m0fwvmzh9RQrFVeeFKbmrxg/3BZ3j6ybeG0s+vxYFXwXCFjKZiuTfgLyGlffAfgWn5NSze3mexXsRlQw4OX970yfTCfawqyziyQ==
Request body:
{
"amount": 100,
"originator": {
"iban": "FI5558498520024222",
"name": "Iban owner name",
"business_id": "FI66554436",
"industry_class_code": "4752"
},
"beneficiary": {
"iban": "FI2159986920069366",
"name": "Virtanen",
"first_names": "Oskari Olavi"
},
"ultimate_originator": {
"name": "Korhonen Pekka Tuomas",
"ssn": "240811-1234",
"business_id": "24",
"industrial_class_code": "08"
},
"message": "Message for receiver",
"reference": "14932116599460307792"
}
Response with status 200:
{
"archive_number": "20180119593064010085",
"status": "paid"
}
Query Parameters
Name | Required | Type | Description |
---|---|---|---|
amount | true | Integer | Amount in euro cents |
originator | true | JSON object | Originator object presenting the payer company |
originator.iban | true | String | The account number in IBAN format |
originator.name | true | String | The account owner name |
originator.business_id | true | String | The business id of the payer company. International format (ALV number). |
originator.industry_class_code | true | String | The industry class code of the payer company |
beneficiary | true | JSON object | Beneficiary object presenting the receiver(payee) |
beneficiary.iban | true | String | The account number in IBAN format |
beneficiary.name | true | String | The account owner name (person last name or company name) |
beneficiary.first_names | false | String | The account owner first names (for person, not required for company) |
ultimate_originator | false | JSON object | Ultimate originator object presenting the party on whose behalf the originator is paying |
ultimate_originator.name | true | String | The name of the ultimate receiver (for person lastname and firstnames or company name) |
ultimate_originator.ssn | false | String | The ssn of the person (required for person) |
ultimate_originator.business_id | false | String | The business_id of the company (required for company). International format (ALV number). |
ultimate_originator.industrial_class_code | false | String | The industrial_class_code of the company (required for company) |
message | false | String | message |
reference | true | String | Payment’s reference number in standard format (viite). If not present ‘message’ is used. |
Response Fields
Name | Type | Description |
---|---|---|
archive_number | String | Archive number of the created payment |
status | String | paid |
Create OP Yrityssiirto payment with ultimate originator
POST https://qa-maksu-api.pivo.fi/other/payments
Response contains archive_number
cUrl Example:
$ curl "https://qa-maksu-api.pivo.fi/other/payments" -d '{"amount":100,"originator":{"iban":"FI5558498520024222","name":"Iban owner name","business_id":"FI66554436","industry_class_code":"4752"},"beneficiary":{"iban":"FI2159986920069366","name":"Virtanen","first_names":"Oskari Olavi"},"ultimate_originator":{"name":"Korhonen Pekka Tuomas","ssn":"240811-1234","business_id":"24","industrial_class_code":"08"},"message":"Message for receiver","reference":"14932116599460307792"}' -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json"
Request Header:
Accept: application/json
Content-Type: application/json
Authorization: Bearer e4d2eb5630c81c9d7f97f997ab2fe47ad21775c51de5642fdf3cf0cf9d9ec8c3
Request body:
{
"amount": 100,
"originator": {
"iban": "FI5558498520024222",
"name": "Iban owner name",
"business_id": "FI66554436",
"industry_class_code": "4752"
},
"beneficiary": {
"iban": "FI2159986920069366",
"name": "Virtanen",
"first_names": "Oskari Olavi"
},
"ultimate_originator": {
"name": "Korhonen Pekka Tuomas",
"ssn": "240811-1234",
"business_id": "24",
"industrial_class_code": "08"
},
"message": "Message for receiver",
"reference": "14932116599460307792"
}
Response with status 200:
{
"archive_number": "20180119593064010085",
"status": "paid"
}
Query Parameters
Name | Required | Type | Description |
---|---|---|---|
amount | true | Integer | Amount in euro cents |
originator | true | JSON object | Originator object presenting the payer company |
originator.iban | true | String | The account number in IBAN format |
originator.name | true | String | The account owner name |
originator.business_id | true | String | The business id of the payer company. International format (ALV number). |
originator.industry_class_code | true | String | The industry class code of the payer company |
beneficiary | true | JSON object | Beneficiary object presenting the receiver(payee) |
beneficiary.iban | true | String | The account number in IBAN format |
beneficiary.name | true | String | The account owner name (person last name or company name) |
beneficiary.first_names | false | String | The account owner first names (for person, not required for company) |
ultimate_originator | false | JSON object | Ultimate originator object presenting the party on whose behalf the originator is paying |
ultimate_originator.name | true | String | The name of the ultimate receiver (for person lastname and firstnames or company name) |
ultimate_originator.ssn | false | String | The ssn of the person (required for person) |
ultimate_originator.business_id | false | String | The business_id of the company (required for company). International format (ALV number). |
ultimate_originator.industrial_class_code | false | String | The industrial_class_code of the company (required for company) |
message | false | String | message |
reference | true | String | Payment’s reference number in standard format (viite). If not present ‘message’ is used. |
Response Fields
Name | Type | Description |
---|---|---|
archive_number | String | Archive number of the created payment |
status | String | paid |
Create OP Yrityssiirto payment with shared secret parameter signature
POST https://qa-maksu-api.pivo.fi/other/payments
Response contains archive_number
cUrl Example:
$ curl "https://qa-maksu-api.pivo.fi/other/payments" -d '{"amount":100,"originator":{"iban":"FI5558498520024222","name":"Iban owner name","business_id":"FI66554436","industry_class_code":"4752"},"beneficiary":{"iban":"FI2159986920069366","name":"Virtanen","first_names":"Oskari Olavi"},"ultimate_originator":{"name":"Korhonen Pekka Tuomas","ssn":"240811-1234","business_id":"24","industrial_class_code":"08"},"message":"Message for receiver","reference":"14932116599460307792","signature":"payment_api_user 312fe4d8055b2e8e1a5cded6d4d53d0d667dc2f1b495c9902a536a6198dbfbf4"}' -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json"
Request Header:
Accept: application/json
Content-Type: application/json
Authorization:
Request body:
{
"amount": 100,
"originator": {
"iban": "FI5558498520024222",
"name": "Iban owner name",
"business_id": "FI66554436",
"industry_class_code": "4752"
},
"beneficiary": {
"iban": "FI2159986920069366",
"name": "Virtanen",
"first_names": "Oskari Olavi"
},
"ultimate_originator": {
"name": "Korhonen Pekka Tuomas",
"ssn": "240811-1234",
"business_id": "24",
"industrial_class_code": "08"
},
"message": "Message for receiver",
"reference": "14932116599460307792",
"signature": "payment_api_user 312fe4d8055b2e8e1a5cded6d4d53d0d667dc2f1b495c9902a536a6198dbfbf4"
}
Response with status 200:
{
"archive_number": "20180119593064010085",
"status": "paid"
}
Query Parameters
Name | Required | Type | Description |
---|---|---|---|
amount | true | Integer | Amount in euro cents |
originator | true | JSON object | Originator object presenting the payer company |
originator.iban | true | String | The account number in IBAN format |
originator.name | true | String | The account owner name |
originator.business_id | true | String | The business id of the payer company. International format (ALV number). |
originator.industry_class_code | true | String | The industry class code of the payer company |
beneficiary | true | JSON object | Beneficiary object presenting the receiver(payee) |
beneficiary.iban | true | String | The account number in IBAN format |
beneficiary.name | true | String | The account owner name (person last name or company name) |
beneficiary.first_names | false | String | The account owner first names (for person, not required for company) |
ultimate_originator | false | JSON object | Ultimate originator object presenting the party on whose behalf the originator is paying |
ultimate_originator.name | true | String | The name of the ultimate receiver (for person lastname and firstnames or company name) |
ultimate_originator.ssn | false | String | The ssn of the person (required for person) |
ultimate_originator.business_id | false | String | The business_id of the company (required for company). International format (ALV number). |
ultimate_originator.industrial_class_code | false | String | The industrial_class_code of the company (required for company) |
message | false | String | message |
reference | true | String | Payment’s reference number in standard format (viite). If not present ‘message’ is used. |
signature | true | String | Calculated signature from other fields. Use dot-notation for nested elements, order alphabetically and calculate hmac as presented in Signature section |
Response Fields
Name | Type | Description |
---|---|---|
archive_number | String | Archive number of the created payment |
status | String | paid |
Signature
Pivo Payment API supports three authentication methods.
- OAuth client credentials
- Parameter signature
- Header signature
You can choose any of the method You like. Use only one method for a single request.
For signature based authentications it is possible to use shared secret
or public key
signing. You can find further information below for implementing your own signature routines.
In Header signature the signature is calculated over the payload (body) as the Parameter signature uses ordered dot notation. Header signature requires Request-Id
header field.
In case of shared secret
you receive the secret from us, however when using public key
please create Your own keys and send the public for Us.
Please consider which level of authentication You need. There exists weakness for OAuth client credentials authentication “Threat: Accidental Exposure of Passwords at Client Site of RFC6819”
Signature Format
Calculated signature format depends whether shared secret or public key is used
Name | Format |
---|---|
shared secret | <your-merchant-identification> <shared-secret-calculated-signature> |
public key | pk:<your-merchant-identification>/<your-public-key-id> <private-key-calculated-signature> |
Shared secret signature
An example of a method that calculates shared secret signature in Ruby
module SignatureTools
def self.sign_signature(account_id, secret, method, path, request_id: nil, params: {}, body: nil)
fail AuthenticationFailed, 'no account_id given' unless account_id
fail AuthenticationFailed, "no secret given for account: #{account_id}" unless secret
message_fields = message_fields(method,
path,
request_id: request_id,
params: params,
body: body)
signature = OpenSSL::HMAC.hexdigest OpenSSL::Digest.new('sha256'),
secret,
message_fields
"#{account_id} #{signature}"
end
def self.message_fields(method, path, request_id: nil, params: {}, body: nil)
message_fields = [method.to_s.upcase, path]
message_fields << request_id if request_id
message_fields += params.except(:signature).sort_by { |k, _| k }
.map { |k, v| "#{k.to_s.downcase}:#{v}" }
message_fields << (body.nil? ? '' : body.is_a?(String) ? body : body.to_json)
message_fields = message_fields.join("\n")
end
end
# example values
account_id = 'test_account'
account_secret = 'secret'
method = 'POST'
path = '/api/payments'
params = { "acquiring_id" => "[Your acquiring id]",
"amount" => "350",
"cancel_url" => "https://yourwebsite.com/callback/cancel",
"merchant_business_id" => "2241007-8",
"merchant_name" => "Pivo Wallet Oy",
"merchant_webstore_url" => "https://pivo.fi/",
"message" => "A Message to Your customer",
"phone" => "",
"reference" => "14932116599460307792",
"reject_url" => "https://yourwebsite.com/callback/reject",
"return_url" => "https://yourwebsite.com/callback/return",
"stamp" => "b5091240-079b-44c4-bb06-9ccbadb81121",
"return_app_url" => "yourscheme://api/return"
}
signature = SignatureTools.sign_signature account_id, account_secret, method, path, params: params
# > signature
# => payment_api_user 2533ebbee2bf2b3408106c1572af2cfc9b3d59c15b85a786c6a796cb24ec1419"
# message fields as reference
# > message_fields
# => "POST\n/api/payments\nacquiring_id:[Your acquiring id]\namount:350\ncancel_url:https://yourwebsite.com/callback/cancel\nmerchant_business_id:2241007-8\nmerchant_name:Pivo Wallet Oy\nmerchant_webstore_url:https://pivo.fi/\nmessage:A Message to Your customer\nphone:\nreference:14932116599460307792\nreject_url:https://yourwebsite.com/callback/reject\nreturn_url:https://yourwebsite.com/callback/return\nstamp:b5091240-079b-44c4-bb06-9ccbadb81121\n"
Steps to calculate the signature
- HTTP method (e.g. ‘POST’) written in capital letters
- HTTP URI path: Plain path, no host (e.g. ‘/api/payments’)
- Request-id is added
- POST parameters sorted in alphabetical order by key. The keys must be written in small letters. The key and the value are joined using a colon (“:”).
- The HTTP body (if any) is added to the string as is.
- The information are combined to one row using the LF character (‘\n’)
⚠ Please note that even an empty value should be added.
⚠ Please note that if your request does not contain an optional parameter it should be omitted from the signature calculation.
Shared secret signature for Form Post API
See section Shared secret signature
Shared secret signature for OP Yrityssiirto
The following lines gives You a starting point when implementing parameter signature for OP Yrityssiirto.
Use the SignatureTools.sign_signature
from previous section and data from below.
Please see the example data. The calculated signature is shown at the end and parameter message_fields for the SignatureTools.sign_signature
method is highlighted to help implementation.
# dotted hash example mixed to hash implementation
module DottedHash
def to_dotted_hash(recursive_key = '')
self.each_with_object({}) do |(k, v), ret|
key = recursive_key + k.to_s
if v.is_a?(Hash)
ret.merge!(v.to_dotted_hash(key + '.'))
else
ret[key] = v
end
end
end
end
::Hash.prepend DottedHash
# ---
account_id = "payment_api_user",
account_secret = "30713f017cf49f1cde8c058446273d02ef040548178a89c050c7aff357729178"
payload_params = {
"amount"=>100,
"originator"=>{:iban=>"FI5558498520024222", :name=>"Iban owner name", :business_id=>"FI66554436", :industry_class_code=>"4752"},
"beneficiary"=>{:iban=>"FI2159986920069366", :name=>"Virtanen", :first_names=>"Oskari Olavi"},
"ultimate_originator"=>{:name=>"Korhonen Pekka Tuomas", :ssn=>"240811-1234", :business_id=>"24", :industrial_class_code=>"08"},
"message"=>"Message for receiver",
"reference"=>"14932116599460307792"
}
payload_dotted_hash = payload_params.to_dotted_hash
# payload_dotted_hash values
# => {"amount"=>100,
# "originator.iban"=>"FI5558498520024222",
# "originator.name"=>"Iban owner name",
# "originator.business_id"=>"FI66554436",
# "originator.industry_class_code"=>"4752",
# "beneficiary.iban"=>"FI2159986920069366",
# "beneficiary.name"=>"Virtanen",
# "beneficiary.first_names"=>"Oskari Olavi",
# "ultimate_originator.name"=>"Korhonen Pekka Tuomas",
# "ultimate_originator.ssn"=>"240811-1234",
# "ultimate_originator.business_id"=>"24",
# "ultimate_originator.industrial_class_code"=>"08",
# "message"=>"Message for receiver",
# "reference"=>"14932116599460307792"}
signature = SignatureTools.sign_signature account_id,
account_secret,
'POST',
'/other/payments',
params: payload_dotted_hash
payload_params['signature'] = signature
# signature value
# => "payment_api_user 312fe4d8055b2e8e1a5cded6d4d53d0d667dc2f1b495c9902a536a6198dbfbf4"
# message_fields in SignatureTools.sign_signature
# => "POST\n/other/payments\namount:100\nbeneficiary.first_names:Oskari Olavi\nbeneficiary.iban:FI2159986920069366\nbeneficiary.name:Virtanen\nmessage:Message for receiver\noriginator.business_id:FI66554436\noriginator.iban:FI5558498520024222\noriginator.industry_class_code:4752\noriginator.name:Iban owner name\nreference:14932116599460307792\nultimate_originator.business_id:24\nultimate_originator.industrial_class_code:08\nultimate_originator.name:Korhonen Pekka Tuomas\nultimate_originator.ssn:240811-1234\n"
Public key signature
Generate a set of private and public keys.
Filename | Explanation |
---|---|
pivo_payment_api_private.key | private key which You use to sign the request |
pivo_payment_api_public.key | public key which You send to Us. With the public key we can verify that the request is signed correctly with the private key. |
See the examples for creating keys and sending public key to Us.
$ cat cert_info
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
C = FI
O = Pivo Payment API
$ openssl req -pubkey -outform PEM -nodes -newkey rsa:2048 -sha1 -x509 -keyout pivo_payment_api_private.key -out pivo_payment_api_public.cert -config cert_info
$ openssl x509 -pubkey -noout -in pivo_payment_api_public.cert > pivo_payment_api_public.key
$ cat pivo_payment_api_public.key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqffh1Fga+zO1CXOSKJTi
ePww/SeAIEDrezztRYpX8NFCVdUvQ+zlezbJNPiGlxVjz/Eyi2nNfAhtOi4UXZse
ivPg95cgKCv9DTB0737Mpw/oPKDmYqmD7W/T/3TZeZWPFNjyK7OAfFvbVMbPD3Ku
0X1iPvczuYm3oU9msDj9SWdN3rUrC+JV6lzsWDvEBLqQykLenNbcuDPyihsPSr60
jE5f3CoT6Tx3egTfBhj7CjO2VtEfrsivn9qaypFOu4dzf5XWUMDrrrOveUCD4BOH
ZDOG9PWlQhVkTvWgtm7OhmqWWsVKCKDOCTHsWmAymMfYLG/6iRzNJ5oGEMZimYTI
jQIDAQAB
-----END PUBLIC KEY-----
# send public key with email to Us
cat public.key | mail -s "<Your company name> public key for pivo payment api" "jari.takalo@op.fi"
We are using the private key pivo_payment_api_public.key
in examples
$ cat pivo_payment_api_private.key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAqffh1Fga+zO1CXOSKJTiePww/SeAIEDrezztRYpX8NFCVdUv
Q+zlezbJNPiGlxVjz/Eyi2nNfAhtOi4UXZseivPg95cgKCv9DTB0737Mpw/oPKDm
YqmD7W/T/3TZeZWPFNjyK7OAfFvbVMbPD3Ku0X1iPvczuYm3oU9msDj9SWdN3rUr
C+JV6lzsWDvEBLqQykLenNbcuDPyihsPSr60jE5f3CoT6Tx3egTfBhj7CjO2VtEf
rsivn9qaypFOu4dzf5XWUMDrrrOveUCD4BOHZDOG9PWlQhVkTvWgtm7OhmqWWsVK
CKDOCTHsWmAymMfYLG/6iRzNJ5oGEMZimYTIjQIDAQABAoIBAQCn5L1FxRY553S/
9lJ9Fby0Z+F4X+l3cslpfeCfHa4KqpqNML20qjBwyshsM8a3PSDQSmB0SDn7eFN0
8gmaAV1lQsyJpdXU+MbJnYEVBjrgF51/Li0Fbo9sHlfacrFUIcEphbeKJZEYxp/c
mVn114IKW4GN0yBb+UbT6Bv2nQTN2Mn578+uu105a++yMSvNvVVjHk7vaq88yQmI
i79tCUl/sZmIChiH52QIvrJ9UAN/yjvkgxi61RJpuxcIFsBuRAAZvpofY8T4MErU
zuwLSg+HMedIia9TvxVY25vgT6WrcQrd8QqqN2u7qaVKfAmh2F8J7LvxF7YP53A2
KnHs9GpBAoGBANtZ7rlfrfKWhJdI88jfZ9AvGPaWo3x3ohN3yp/jR6D+GQNEpc3v
Oa5w+ws0oS8QHrYjhPDEuF6aUd/QvEPZNGJO3ImYPKXub0DVVfzYmT2eRvD/0iza
1JQ3aFNZp1KzaxxJ2IPlIMG0hLbN+dmzFPmLZ0BiFMlE3FVvz+GdvRRdAoGBAMZd
vtdS7LCYN5EtylX/+g2kJT5rHrwtAIYcEpx5WS202Nunc2CsVW0Gu+dggLgC9v3b
PFg9HLd6glEygiam+WX1jOlQRADNHEE5EEb/awdUSn6TuXaucptpMCPcVA2Szmqi
yBkQDkZId7mqn1iuI3UkgUmQz4Je5bFdqMlULUHxAoGADHX4bMl1rUFFuP5o8vFW
CWqThXabRPfa2jZE6X5U1F16+EePD7rWUQWUDVfpDC5jQD+WTsMuIhmsKwXFnCkb
o7YNKzfdwiNgaJN19g4r7mcuaoRt4SRxTRY0s/901OriW9IKHbwHd1mU7HmZBvf4
ahC0ReEMgz6JMcVPTubSFfUCgYEAmJklZp/0VSXEGXsQXHg+9J+BxzLAwiIcdQ7D
gnwXjoHBEbkPmR752JWFl8k1PFmLiF5PXdIldUBZX/1rAjERSs5LSHDm33bqdYJg
cz2qKEk/xX/+/L3WjJeu4OduAcMd6AqxxBYF5St6bupDCwVrYYJjsyQjfjdim79h
SP/okQECgYBXAqtou1Y0IPCsO7OZVxm0v1bdQXwaRmmSnzMFaJMGGO4xnpfOAcjL
1nC2XJG26recgoX7wajeeJT0ByukWdf2DCjMR1waLGhJw9IJSwXUbKe4A2hHNx10
7mo94Tf+lu3zoc+UX3YqvL7YPa/8Rtv8b/FP31V1Zve1n4lHHlxkwQ==
-----END RSA PRIVATE KEY-----
Public key parameter signature signing example with ruby
def SignatureTools.sign_pki_signature(account_id, key_id, private_key, method, path, request_id: nil, params: {}, body: nil)
fail AuthenticationFailed, 'no account_id given' unless account_id
fail AuthenticationFailed, "no private_key given for account: #{account_id}" unless private_key
message_fields = message_fields(method,
path,
request_id: request_id,
params: params,
body: body)
signature = private_key.sign(OpenSSL::Digest::SHA256.new,
message_fields)
signature = Base64.strict_encode64(signature)
signature = "pk:#{account_id}/#{key_id} #{signature}"
Rails.logger.debug { "Message: \n#{message_fields}" }
Rails.logger.debug { "Calculated pki signature: #{signature} for\n account_id: #{account_id}\n with key id: #{key_id} \n method: #{method}\n path: #{path}\n params: #{params.to_json}\n body: #{body}\n." }
signature
end
test_private_key =
<<-END
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAqffh1Fga+zO1CXOSKJTiePww/SeAIEDrezztRYpX8NFCVdUv
Q+zlezbJNPiGlxVjz/Eyi2nNfAhtOi4UXZseivPg95cgKCv9DTB0737Mpw/oPKDm
YqmD7W/T/3TZeZWPFNjyK7OAfFvbVMbPD3Ku0X1iPvczuYm3oU9msDj9SWdN3rUr
C+JV6lzsWDvEBLqQykLenNbcuDPyihsPSr60jE5f3CoT6Tx3egTfBhj7CjO2VtEf
rsivn9qaypFOu4dzf5XWUMDrrrOveUCD4BOHZDOG9PWlQhVkTvWgtm7OhmqWWsVK
CKDOCTHsWmAymMfYLG/6iRzNJ5oGEMZimYTIjQIDAQABAoIBAQCn5L1FxRY553S/
9lJ9Fby0Z+F4X+l3cslpfeCfHa4KqpqNML20qjBwyshsM8a3PSDQSmB0SDn7eFN0
8gmaAV1lQsyJpdXU+MbJnYEVBjrgF51/Li0Fbo9sHlfacrFUIcEphbeKJZEYxp/c
mVn114IKW4GN0yBb+UbT6Bv2nQTN2Mn578+uu105a++yMSvNvVVjHk7vaq88yQmI
i79tCUl/sZmIChiH52QIvrJ9UAN/yjvkgxi61RJpuxcIFsBuRAAZvpofY8T4MErU
zuwLSg+HMedIia9TvxVY25vgT6WrcQrd8QqqN2u7qaVKfAmh2F8J7LvxF7YP53A2
KnHs9GpBAoGBANtZ7rlfrfKWhJdI88jfZ9AvGPaWo3x3ohN3yp/jR6D+GQNEpc3v
Oa5w+ws0oS8QHrYjhPDEuF6aUd/QvEPZNGJO3ImYPKXub0DVVfzYmT2eRvD/0iza
1JQ3aFNZp1KzaxxJ2IPlIMG0hLbN+dmzFPmLZ0BiFMlE3FVvz+GdvRRdAoGBAMZd
vtdS7LCYN5EtylX/+g2kJT5rHrwtAIYcEpx5WS202Nunc2CsVW0Gu+dggLgC9v3b
PFg9HLd6glEygiam+WX1jOlQRADNHEE5EEb/awdUSn6TuXaucptpMCPcVA2Szmqi
yBkQDkZId7mqn1iuI3UkgUmQz4Je5bFdqMlULUHxAoGADHX4bMl1rUFFuP5o8vFW
CWqThXabRPfa2jZE6X5U1F16+EePD7rWUQWUDVfpDC5jQD+WTsMuIhmsKwXFnCkb
o7YNKzfdwiNgaJN19g4r7mcuaoRt4SRxTRY0s/901OriW9IKHbwHd1mU7HmZBvf4
ahC0ReEMgz6JMcVPTubSFfUCgYEAmJklZp/0VSXEGXsQXHg+9J+BxzLAwiIcdQ7D
gnwXjoHBEbkPmR752JWFl8k1PFmLiF5PXdIldUBZX/1rAjERSs5LSHDm33bqdYJg
cz2qKEk/xX/+/L3WjJeu4OduAcMd6AqxxBYF5St6bupDCwVrYYJjsyQjfjdim79h
SP/okQECgYBXAqtou1Y0IPCsO7OZVxm0v1bdQXwaRmmSnzMFaJMGGO4xnpfOAcjL
1nC2XJG26recgoX7wajeeJT0ByukWdf2DCjMR1waLGhJw9IJSwXUbKe4A2hHNx10
7mo94Tf+lu3zoc+UX3YqvL7YPa/8Rtv8b/FP31V1Zve1n4lHHlxkwQ==
-----END RSA PRIVATE KEY-----
END
account_id = 'payment_api_user'
key_id = 'ae4c6330abae2d13d646895bc397b0030ed378d0dada0a29907c1e5ae83e0731'
private_key = OpenSSL::PKey::RSA.new(test_private_key)
params =
{"amount"=>100,
"reference"=>"1232",
"message"=>"testi viesti",
"originator.iban"=>"FI5558498520024222",
"originator.name"=>"Ahtelaisen ahkiot ja alariippulukot",
"originator.business_id"=>"FI66554436",
"originator.industry_class_code"=>"4752",
"ultimate_originator.name"=>"Virtanen Oskari Olavi",
"ultimate_originator.ssn"=>"240811-1234",
"ultimate_originator.business_id"=>"24",
"ultimate_originator.industrial_class_code"=>"08",
"beneficiary.iban"=>"FI2159986920069366",
"beneficiary.name"=>"Virtanen",
"beneficiary.first_names"=>"Oskari Olavi"}
signature = SignatureTools.sign_pki_signature account_id,
key_id,
private_key,
'POST',
'/other/payments',
params: params
# signature
# => "pk:payment_api_user/ae4c6330abae2d13d646895bc397b0030ed378d0dada0a29907c1e5ae83e0731 k0CsOV/U0rxPxefEhuxyTuyvmBZCCqbDip6eN4l+UchqesCi+86XtdTirV04KEEE2B0GLVYx+SI9Ryue9sOt3xGjfAQz4ZpH5PE9xx/YKwVQ3B6Bu1bZwTVLOf5EPiAkMKgNjVSqbndIH4iBHPYjvXLmKytTs6kryJ4IOZ4DXd0vohy/paZe/TBqjDvdLcBZVNOix86RmE7rJ62y4S+FKUxNYavb0OVDEH+9LFrsMuFpQ9TQYcAQuT6St7q5qbGKcJ8PyFgJHABt8mAJMrKO45MrNUBylx7UauAW+6TB/rDQhfBnNZ2EmJc7LbjINoFah9rwwEZnZiTac0Q7IE2CKw=="
Public key header signature signing example with ruby
See the example request
account_id = 'payment_api_user'
key_id = 'ae4c6330abae2d13d646895bc397b0030ed378d0dada0a29907c1e5ae83e0731'
private_key = OpenSSL::PKey::RSA.new(test_private_key)
request_id = '8eb15bc6-b6fa-497d-adf4-97f067ea052f'
payload_params = {
"amount"=>100,
"originator"=>{:iban=>"FI5558498520024222", :name=>"Iban owner name", :business_id=>"FI66554436", :industry_class_code=>"4752"},
"beneficiary"=>{:iban=>"FI2159986920069366", :name=>"Virtanen", :first_names=>"Oskari Olavi"},
"ultimate_originator"=>{:name=>"Korhonen Pekka Tuomas", :ssn=>"240811-1234", :business_id=>"24", :industrial_class_code=>"08"},
"message"=>"Message for receiver",
"reference"=>"14932116599460307792"
}
signature = SignatureTools.sign_pki_signature account_id,
key_id,
private_key,
'POST',
'/other/payments',
request_id: request_id,
body: payload_params.to_json
# message_fields
# => "POST\n/other/payments\n8eb15bc6-b6fa-497d-adf4-97f067ea052f\n{"amount":100,"originator":{"iban":"FI5558498520024222","name":"Iban owner name","business_id":"FI66554436","industry_class_code":"4752"},"beneficiary":{"iban":"FI2159986920069366","name":"Virtanen","first_names":"Oskari Olavi"},"ultimate_originator":{"name":"Korhonen Pekka Tuomas","ssn":"240811-1234","business_id":"24","industrial_class_code":"08"},"message":"Message for receiver","reference":"14932116599460307792"}"
# puts "#{message_fields}"
#POST
#/other/payments
#8eb15bc6-b6fa-497d-adf4-97f067ea052f
#{"amount":100,"originator":{"iban":"FI5558498520024222","name":"Iban owner name","business_id":"FI66554436","industry_class_code":"4752"},"beneficiary":{"iban":"FI2159986920069366","name":"Virtanen","first_names":"Oskari Olavi"},"ultimate_originator":{"name":"Korhonen Pekka Tuomas","ssn":"240811-1234","business_id":"24","industrial_class_code":"08"},"message":"Message for receiver","reference":"14932116599460307792"}
#
# signature
# => "pk:payment_api_user/ae4c6330abae2d13d646895bc397b0030ed378d0dada0a29907c1e5ae83e0731 eUotQ2nhZRok8UNH34z8VSeLpfCWoFIwddxHvwjvttZErQk33rM5rii+nkB7ZRrpwkC/lRXymSHWAzhBThhdYwVB6Hzk77ljmFObebla8hXuaKFcqBMrG0LkkBx83BNiLVDr31VxgYqMYnyd7jluJTjKcfzCsfNssrOHb9BfhZfzD4x1Gysn8nIkkXfm9eeIgASFtHNA+jBQddon9c+D3BrwpOe3zlqz+15tzkhJ1zScrlLVKU7m0fwvmzh9RQrFVeeFKbmrxg/3BZ3j6ybeG0s+vxYFXwXCFjKZiuTfgLyGlffAfgWn5NSze3mexXsRlQw4OX970yfTCfawqyziyQ=="