NAV Navbar
Logo

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.

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

  1. HTTP method (e.g. ‘POST’) written in capital letters
  2. HTTP URI path: Plain path, no host (e.g. ‘/api/payments’)
  3. Request-id is added
  4. 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 (“:”).
  5. The HTTP body (if any) is added to the string as is.
  6. 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=="