OVO Partner Integration Documentation

Technical Documentation SNAP - Customer Topup

SNAP Customer Top Up v1.14

Overview of OpenAPI for Customer Topup Use Case

Host :

  • Staging : https://app.byte-stack.net
  • Production : https://apigw.ovo.id
Method Endpoint Usage
POST HOST/OVOSNAP/v1.0/access-token/b2b Generate System Token (B2B)
POST HOST/OVOSNAP/v2.0/emoney/account-inquiry Customer Topup Inquiry
POST HOST/OVOSNAP/v2.0/emoney/topup Customer Topup
POST HOST/OVOSNAP/v2.0/emoney/topup-status Customer Topup Inquiry Status
GET HOST/cashin/snap/v2.0/merchant/account/balance Customer Topup Check Balance Deposit

Generate System Token (B2B)

Partner with client-specific use cases (non-user-related use-case) can use the below API to generate system tokens.

Before generating the token, partners are requested to address this as a requirement, so OVO can configure the necessary.

As per BI requirements, the token expiry for B2B token should be 15 minutes
Not recommended to call System Token (B2B) every time transaction happen

Endpoint: HOST/OVOSNAP/{version}/access-token/b2b

Header Request

Parameter Data Type Mandatory Description
X-CLIENT-KEY String M Client’s client_id (PJP Name) (given at completion registration process )
Content-Type String M String represents indicate the media type of the resource (e.g. application/json, application/pdf)
X-SIGNATURE String M Using Asymmetric Signature
X-TIMESTAMP String M Client's current local time in yyyy-MM-ddTHH:mm:ss.SSSTZD format

Body Request

Parameter Data Type Mandatory Description
grantType String M "client_credentials”: The client can request an access token using only its client credentials (or other supported means of authentication) when the client is requesting access to the protected resources under its control

Body Response

Parameter Data Type Description
responseCode String Refer to standar data dan spesifikasi teknis part 6 (Response Code).If access token failed to generate, this value must be filled.
responseMessage String Refer to standar data dan spesifikasi teknis part 6 (Response Message)If access token failed to generate, this value must be filled.
accessToken String (2048) A string representing an authorizationissued to the client that used to accessprotected resources
tokenType String The access token type provides the client with the information required to successfully utilize the access token to makea protected resource request (along with type-specific attributes)

Token Type Value:
  • "Bearers”: includes the access token
String in the request
  • "Mac": issuing a Message Authentication Code (MAC) key together with the access token that is used to sign certain components of the HTTP requests
expiresIn String Session expiry in seconds

Sample Request

curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v1.0/access-token/b2b' \
    --header 'x-client-key: test' \
    --header 'Content-Type: application/json' \
    --header 'X-Signature: 14fc9b92ce30559b3db75987b0150890e2680fe5dc8e2eda9ed77304603dcf0670f41ff1de1b3d5a79e417f39391abb694f41028b1a425f2d40e7eccf988a5f281f96afea63a8cfe7f01938b7d67a76070b7fb3a7c6cdfe1f94e6e98e502db8cdc8a7db0aa08186ae44c28197a3b2f66dd8170d0a1c848f0a424a6d857ecbf8337cefa7120b5e4a04bd52f4cc9a6ae5a3bd9c8125227b5a259cd03ed7c4ea3afef9f147f3d73b99b3c1e6df68011b485ed0c177133c92d8cce913f14bb10da8223783c8b94b0c0a65b07fdeac0f3687d86a95bb4389f4e15b5679119dee0679823dd8db5e28af728c32600d1a2af48871ec2134dc5ac6ede056fb7d557d5d824' \
    --header 'x-Timestamp: 2022-08-02T02:03:08.123Z' \
    --data-raw '{
        "grantType" : "client_credentials"
    }'

Sample Response

{
    "responseCode": "2007300",
    "responseMessage": "Success",
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6IlptVTRORGt5WVRnM01EZ3lOR1JpWmprMU5HSTBZakE1TURVMU1UWmhOV0kiLCJyYW5kb20iOiJOalF3TXpFMk5RIiwidmVyc2lvbiI6MX0.Wo5-5jM07X6Ft9UjZx0u2H9egmu76VwXoZzxcB_noSQ",
    "tokenType": "Bearer",
    "expiresIn": "900"
}

Transaction API Headers

All the APIs mentioned are to follow the standard header format mentioned here, unless stated otherwise

Parameters Description Attribute Example
Content-type String represents indicating the media type of the resource Mandatory application/json,application/pdf
Authorization Represents access_token of a request;string starts with keyword “Bearer ”followed by accessToken (e.g. BearereyJraWQiOi...JzcIiwiY) No Need For
  1. Request B2B Access Token API
Mandatory For
  1. Customer Topup Inquiry
  2. Customer Topup
  3. Customer Topup Inquiry Status
  4. Customer Topup Check Balance
Bearer gp9HjjEj813Y9JGoqwOeOPWbnt4CUpvIJbU1mMU4a11MNDZ7Sg5u9a
X-PARTNER-ID Client's client_id. Mandatory oamerchantg
X-TIMESTAMP Client's current local time in yyyyMMddTHH:mm:ss.SSSTZD format Mandatory
X-SIGNATURE
  1. Use Asymmetric for Token request
  2. Use Symmetric for all transaction header
Mandatory Please check Signature Section
X-DEVICE-ID Device identification on which the API services are currently being accessed by the end-user (customer) Optional
X-EXTERNAL-ID Unique ID to avoid duplication. ID reset for every 24 hours    Use this for 
  • Customer Topup Inquiry
  • Customer Topup
  • Customer Topup Inquiry Status
  • Customer Topup Check Balance
Mandatory
CHANNEL-ID PJP’s channel id Device identification on which the API services is currently being accessed by the end user (customer) Optional

Error Code

Here is the list of authentication related common error codes that can be returned throughout all APIs. Please note that the “XX” in the middle of the error code will follow the respective service code of the API. Except for the APIs that are not part of BISNAP, but are using the same BISNAP authentication mechanism, for this kind of API “XX” will stay as it is.

HTTP Code Error Code Error Message (Indonesian)
401 401XX00 Unauthorized. [reason]
401 401XX01 Invalid Token:Anda Tidak Memiliki Akses

Sample Response

{
   "data": {
      "actionables": [
         {
           "pinWebviewURL": "https://webview.byte-stack.net/cellblockui/v2/paymentPin",
           "qParams": {
             "action": "regeneratePayment",
             "client-id": "snapinternalcimb"
             },
           "token": "PaymentToken"
         }
      ],
      "error": {
         "code": "OV00502",
         "message": "Unauthorized access"
      }
   },
   "responseCode": "4013701",
   "responseMessage": "Invalid Token:Anda Tidak Memiliki Akses"
}
{
   "responseCode": "4013700",
   "responseMessage": "Unauthorized. No Interface Definition: Timestamp."
}
{
   "responseCode": "4013700",
   "responseMessage": "Unauthorized. Signature Failed: AGW-002-05."
}

Signature Asymmetric

Signature asymmetric is used by OVO to verify that your access token request is not altered by attackers.

Generate Signature Asymmetric for Header of Access Token B2B / B2B2C

SHA256withRSA is used to generate the signature with your Private Key as the key.

X-SIGNATURE = SHA256withRSA(PrivateKey, StringToSign)

The StringToSign will be a colon-separated list derived from some request data as below:

StringToSign = HTTPMethod + ”:“+ EndpointUrl +":“ + Lowercase(HexEncode(SHA-256(minify(RequestBody)))) + ":“ + TimeStamp

Signature Symmetric

Signature is used to verify that your open API service request is not altered by attackers.

Generate Signature Symmetric

SHA-512 HMAC is used to generate the signature with your Client Secret as the key.

X-SIGNATURE = HMAC-SHA512(ClientSecret, StringToSign)

The StringToSign will be a colon-separated list derived from some request data as below :

stringToSign = HTTPMethod + ”:“+ EndpointUrl +":"+ AccessToken + ":“+ Lowercase(HexEncode(SHA-256(minify(RequestBody)))) + ":“ + TimeStamp
Notes:
  1. Endpoint URL complete including all parameters on URL related
  2. For parameters minify (Request Body), in case there is no Request Body then use the empty string
How to Generate PKCS1 Private Key and Public Key
Generate Private Key:
openssl genrsa -out private.pem 4096
Generate PKCS1 Public Key:
openssl rsa -in private.pem -RSAPublicKey_out -out public.pem

Error Code

HTPP Code Error Code Error Message (Indonesian)
401 4017300 Unauthorized. [HMAC mismatch]
400 4007301 Invalid timestamp format [X-TIMESTAMP]
400 4017300 Invalid field format [clientId/clientSecret/grant_type]

SNAP Auth Validator Utility

This snippet code is an example on how to check SNAP Auth validity

package main

import (
    "crypto"
    "crypto/hmac"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/sha512"
    "crypto/x509"
    "encoding/hex"
    "encoding/pem"
    "errors"
    "fmt"
    "strings"
)

const PrivateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAghghSSsbhCKBllAWbLmqdvqkg/b/yrRXE3PHzH2zkOVUgV/u
YtmLAaMUOt4UGiBKQa4TcRPPf6ITQpHCrpgpCadQAvikTFF+BW7pBdSvfZQVsscf
9MGLFkz7+uQlcnrDotHjeQ1Ei4wWU/kqe5rLRJZMl2+91yxWeaLTgockF6Mb8XrR
DI4DdaxBY0Lr0B20QqOFgM54ti1i628vAQGFFZPw2rgKufLOmRkJ9A8+vjVhXxC3
8sB2Is81okJcQKsLQZT4IqLxayF8jfr2j9mZtTCpoBdjQIVcbCbOFKtNN7c1j7wy
c8T9lWPPY43vKzfNf73w9JzKLfH6M8LDfJFIzwIDAQABAoIBAAsi2aaTxBU5hvJB
BMpl0kvBBNgvCpJlgZausIm2sOpUVzmD5robeSS4uwCXBg1+ehzJM+zYD0kTrKZk
J9AeQlULN3QpqJbH2wvIBLZ1EFilln3pQbkH4EoWaPN/GB2GmLyVTu2LzzRK15Z0
m8hc5c2HOCM2c3+50eUzpPtlaheDxRP5STPZVVpz87UyK09FAstzGNYmzZ733lDV
JoVGaa7hWTxzm4M4oxo2Bb06FSW2g3HKkqWC8PGBQ4nxDo7Xy3AkEYI3fGsYXbjC
fn/xMly/ARFWM1sICJTHzNdpHPIBuYtRJ8bEgc1Pew8g+JfAA1BFCWvYqcwjxIr9
6+JEfsECgYEA3AQdKGGq2sSPghbBbeWMotXxzXtwxJjxFAvRR32riIiu/qdtoC29
rsBpjvSs4jFVd4II6cE7xy0xJLtOU6asNIfhJbL3k3/f+HCN2PzkHwsNHIG6Xb8v
m9W+YCJudOmQJzfWUDcwfYk+uL0f6DnhGpazr5HkPmvicVzEsV3cRBECgYEAl18Q
8SwA8qwf18wCbVTgX/u2XD33lnabnz8a4F/5N4pKs7FKPDz7bgW4Jyik9+THZsrG
zepw8wXxzNqyTLC0WmyyYLTNp1x7EUBfAM40QYjydXcbXrTti28OGAKTh/DB3OvO
vl9uO+FfwWFXIdVK4Ie8JWsFrn5ciBM04aY4Ht8CgYBgeiVXCczj9YGAZ/4V9KzA
0tQfaNvAOditE6mHkeHgEx+5Zy25KZWdxZ4EI+KTpVJ2/zxtVGCkLHr6QnBMWi/1
MQhXgazyrwZFaQWqeuqFelEbiP9yEF4OFaJPgYmyFqExsVh3AFxxD/fDBpuxN4Aw
KplMicruXFyFnUpbBG+MIQKBgGFZfmfcSO/IyuHaDmWKBJM2Kt2/7I8T0Jnl178d
egXCJrDSAFAlV/42J2znstDKjYMKPjkH4YQp+owoyiqQKi1NYprXLLvJukwp/e9i
rjDHhkcNRsjtyye1UHcYkREIQWV3Mgs1DIvuMcsIcyULK5CjOtlFru29znyk/Ylx
gP45AoGBAIBkUjJo+xd7sNNhNCW07rUiAWegEHOIk5pWzwLDlY2AVK9/tlNBI6wD
GWLT6L5B9reN+982tOEBAYlP/p2e+eUBy8BPgvqbsPD3yHD3pR8WibrNEVwGhL8P
SC/C9azKvNlBsN1HRWdRrEN9Fx5Eu15XNBlKX1RIiHkv8iW1n6F9
-----END RSA PRIVATE KEY----
`

const PublicKey = `-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAghghSSsbhCKBllAWbLmqdvqkg/b/yrRXE3PHzH2zkOVUgV/uYtmL
AaMUOt4UGiBKQa4TcRPPf6ITQpHCrpgpCadQAvikTFF+BW7pBdSvfZQVsscf9MGL
Fkz7+uQlcnrDotHjeQ1Ei4wWU/kqe5rLRJZMl2+91yxWeaLTgockF6Mb8XrRDI4D
daxBY0Lr0B20QqOFgM54ti1i628vAQGFFZPw2rgKufLOmRkJ9A8+vjVhXxC38sB2
Is81okJcQKsLQZT4IqLxayF8jfr2j9mZtTCpoBdjQIVcbCbOFKtNN7c1j7wyc8T9
lWPPY43vKzfNf73w9JzKLfH6M8LDfJFIzwIDAQAB
-----END RSA PUBLIC KEY-----
`

const RawBody = `{"foo": "bar"}`

func main() {
    specs := Specs{
        ClientID: "test",
        PrivateKey: PrivateKey,
        PublicKey: PublicKey,
        ClientSecret: "foo-bar",
        HTTPMethod: "POST",
        URI: "/foo/bar",
        RawBody: []byte(RawBody),
        Timestamp: "2022-03-10T04:02:11.108+07:00",
        AccessToken: "foobar",
    }

    AccessTokenSignature := "730f96f44e95c3b9fe7cb3ed94efa0cf8424813c0aa5974bd1c66ca0dcd00150107decca4de63b5b776cc7f7db25cf67ba70740438af76776bd15cba75e4604ae97e8b27f8ce36b87ad34ae6eaf4c3390a80db7e353b8384c85d8bf95ff8095e183d45520d739e12228fdbdf673b361ab97ac6de1bd544fd9b850d3cb5f91e47d6d7975565004d7551e23ca5bf42db9090e200f65789eaa74ff50ecb3a316786831d0f89e66230fcbd7e7195ba9b5f40f46b3b7001ab6b8f47d7be68656f5be0a8ff4a51bebb2c2741e997ca33adcec1ec58137af5a61b6234f7791399be6c1fcdfd1c2650a39d29ff8c38c6350754255b4970db1f44da43175798637b47784c"
    SymmetricSignature := "9b4ad98c7e4107ac7008576fb0347eb2369d540f929435a438d81094d4cb6b65348e06cb9af4660deb86a4508694f8431b30351e57f9874662e3740779fcdc4b"
    AsymmetricSignature := "533c8f937b285ac9d258f85b01d48ad8a1cf66d7418686dcd030bb6d6775bd88b0526d4abe92e8f3a2142efd8905e4fa77460a5affc064b57a3607dc6849d3fb3b7895d425e027f01f43f067c8bfcd1c25a039d2d9d3a8f1df36ab9d2607778b6d1f846e2f3e606aa87c8eca9b1a6a4bd35b072ea7beb0ec80714594ade85eede4992892eaaa1f3a46f48cd17f2b6e46753e76546fa39990e0f4e7a5f077f2586092af0bfbff38ceceebf7dfcafe2e1a8c3b3cca003d4802aebb1f659b40bab08f43aaa9349df9d69e6c3e88cae7b5a1721bf1c06f9c081e10fe6f8ef087fd4e3e616a4ae35040612a4abdbda211f02a386d07a988906a06e2bd29c1722583f3"

    err := Validate(AccessTokenSignature, AccessToken, specs)
    if err != nil {
        println(err.Error())
    } else {
        println("Validation success.")
    }

    err = Validate(SymmetricSignature, Symmetric, specs)
    if err != nil {
        println(err.Error())
    } else {
    println("Validation success.")
    }

    err = Validate(AsymmetricSignature, Asymmetric, specs)
    if err != nil {
        println(err.Error())
    } else {
        println("Validation success.")
    }
}

type ValidationType string

const (
    AccessToken ValidationType = "access-token"
    Symmetric = "symmetric"
    Asymmetric = "asymmetric"
)

var (
    ErrInvalidHMAC = errors.New("invalid HMAC")
    ErrInvalidValType = errors.New("invalid validation type")
)

type Specs struct {
    ClientID string
    PrivateKey string
    PublicKey string
    ClientSecret string
    HTTPMethod string
    URI string
    RawBody []byte
    Timestamp string
    AccessToken string
}

func Validate(signature string, valType ValidationType, specs Specs) error {
    switch valType {
        case AccessToken:
            return validateAccessTokenSignature(signature, specs)

        case Symmetric:
            return validateSymmetricSignature(signature, specs)

        case Asymmetric:
            return validateAsymmetricSignature(signature, specs)

        default:
            return ErrInvalidValType
    }
}

func validateAccessTokenSignature(signature string, specs Specs) error {
    data := fmt.Sprintf("%s|%s", specs.ClientID, specs.Timestamp)

    decodedSignature, err := hex.DecodeString(signature)
    if err != nil {
         return err
    }

    ok, err := SHA256WithRSAValidate(decodedSignature, []byte(specs.PublicKey), []byte(data))
    if err != nil {
         return err
    }

    if !ok {
        return ErrInvalidHMAC
    }

    return nil
}

func validateSymmetricSignature(signature string, specs Specs) error {

    transformedBody, err := transformBody(specs.RawBody)
    if err != nil {
         return err
    }
    
    data := fmt.Sprintf("%s:%s:%s:%s:%s", specs.HTTPMethod, specs.URI, specs.AccessToken, transformedBody, specs.Timestamp)
    
    ok := SHA512HMACValidate([]byte(data), []byte(signature), []byte(specs.ClientSecret))
    if !ok {
         return ErrInvalidHMAC
    }
    
    return nil
}

func validateAsymmetricSignature(signature string, specs Specs) error {
    transformedBody, err := transformBody(specs.RawBody)
    if err != nil {
         return err
    }
    
    data := fmt.Sprintf("%s:%s:%s:%s", specs.HTTPMethod, specs.URI, transformedBody, specs.Timestamp)
    
    decodedSignature, err := hex.DecodeString(signature)
    if err != nil {
         return err
    }
    
    ok, err := SHA256WithRSAValidate(decodedSignature, []byte(specs.PublicKey), []byte(data))
    if err != nil {
         return err
    }
    
    if !ok {
         return ErrInvalidHMAC
    }
    
    return nil
}

func transformBody(body []byte) (string, error) {
    sha256Body := sha256.Sum256(body)
    hexBody := hex.EncodeToString(sha256Body[:])
    finalBody := strings.ToLower(hexBody)
    
    return finalBody, nil
}

func SHA256WithRSAValidate(sig, pubPEM, data []byte) (bool, error) {
    hashed := sha256.Sum256(data)
    
    block, _ := pem.Decode(pubPEM)
    if block == nil {
         return false, errors.New("failed to decode public PEM")
    }
    
    pub, err := x509.ParsePKCS1PublicKey(block.Bytes)
    if err != nil {
         return false, err
    }
    
    
    err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, hashed[:], sig)
    if err != nil {
         if errors.As(err, &rsa.ErrVerification) {
         return false, nil
         }
        
         return false, err
    }
    
    return true, nil
}

func SHA512HMACValidate(message, messageMAC, key []byte) bool {
    mac := hmac.New(sha512.New, key)
    mac.Write(message)
    expectedMAC := mac.Sum(nil)
    expectedMacEncoded := hex.EncodeToString(expectedMAC)
    
    return hmac.Equal(messageMAC, []byte(expectedMacEncoded))
}

Customer Topup Inquiry

Diagram

This API will be used by Merchants to get the user information before initiating Top Up

Your request must contain the following information:

Request Body

Parameter Data Type Mandatory Description
partnerReferenceNo String M Unique Transaction identifier on service consumer system. Value needs to be the same with the top-up call.Max: 64 chars
customerNumber String M Customers OVO Account Number which want to Top Up. Its Mandatory for Customer Top Up
>amount Object M
>>value String (ISO4217) M Net amount of the transactions. If it's IDR then the value includes 2 decimal digits. eg IDR 10,000,- will be placed with 10000.00
>>currency String M Currency (IDR)
transactionDate yyyy-MM-ddThh:mm:ss+07:00 O transactions date : ISO 8601
>additionalInfo Object O Additional information
>>preInquiryFlag String O Value = Y/N
Default = N

Y -> For read only, meaning the Customer Topup inquiry data will not be used for customer top up validation.

N -> Customer Topup Inquiry To be used for Customer Top Up Validation

*usually value = N will be used the same with omitting this field, value = Y is only used when special events need to be catered for example when partners have some function to do Topup Inquiry without doing top up later on.
>>senderInstitutionID String O Contains the ID of Sender Institution that is a unique identifier for institutions like Banks or Collecting Agents. This could be a Transfer Bank Code, Collecting Agent Code, or any other unique code assigned to these institutions. This will need to be mapped to Institution Name at OVO side, and the name will be shown in OVO User Transaction History Detail.

Response Body

Parameter Data Type Mandatory Description
responseCode String M Response code
responseMessage String M Response description
referenceNo String C Transaction identifier on service provider system. Be filled upon successful transactions
partnerReferenceNo String M Transaction identifier on service consumer system
customerNumber String M Customers Account Number Rule of mask for UI XXXXXXXXX1857
customerName String C Customers account name Rule of mask for UI: - 1 char = A - 2 chars = AA - 3 chars = A** - 4 chars = A A** - > =5 chars = AA**A
customerMonthlyInLimit Numeric C Customers monthly cash-in limit
>minAmount Object O
>>value String (ISO4217) M Net amount of the transactions. If it's IDR then the value includes 2 decimal digits. eg IDR 10,000,- will be placed with 10000.00
>>currency String M Currency
>maxAmount Object C
>>value String (ISO4217) M Net amount of the transactions. If it's IDR then the value includes 2 decimal digits. eg IDR 10,000,- will be placed with 10000.00
>>currency String M Currency
>amount Object C
>>value String (ISO4217) M Net amount of the transactions. If it's IDR then the value includes 2 decimal digits. eg IDR 10,000,- will be placed with 10000.00
>>currency String M Currency
>feeAmount Object C
>>value String (ISO4217) M Fee amount of transaction.
>>currency String M Currency
feeType String O Fee type (Admin Fee)
>additionalInfo Object O Additional information
>>preInquiryFlag String O From the request
>>responseInfo1 String O Inquiry response additional info 1
>>senderInstitutionID String O From the request

Notes:

For partnerReferenceNo field, in cases where it is not possible for the partner to ensure the same partnerReferenceNo between inquiry & top-up call, then just fill this with any unique reference for easier investigation purposes. And let the OVO team know so that it is configured accordingly.

Sample Request

{
    "partnerReferenceNo": "20220728000000001",
    "customerNumber": "080000000001",
    "amount": {
        "value": "100000.00",
        "currency": "IDR"
    },
    "transactionDate": "2022-07-28T10:00:00+07:00",
    “additionalInfo”: {
        “preInquiryFlag”: “N”
        “senderInstitutionID“: “999”
    }
}

Sample Response

{
    "responseCode": "2003700",
    "responseMessage": "Request has been processed successfully",
    "referenceNo": "20220728000000001",
    "partnerReferenceNo": "20220728000000001",
    "customerNumber": "XXXXXXXX0001",
    "customerName": "C**tomer Na**",
    "customerMonthlyInLimit": "40000000",
    "minAmount": {
        "value": "10000.00",
        "currency": "IDR"
    },
    "maxAmount": {
        "value": "20000000.00",
        "currency": "IDR"
    },
    "amount": {
        "value": "100000.00",
        "currency": "IDR"
    },
    "feeAmount": {
        "value": "1000.00",
        "currency": "IDR"
    },
    "feeType": "Admin fee",
    “additionalInfo”: {
        “preInquiryFlag”: “N”
        “senderInstitutionID”: “999”
    }
}

Response Code

HTTP Code Response Code Error Message (English) Remarks
200 2003700 Request has been processed successfully Request success
403 4033718 Customer Account Not Found Request failed
403 4033705 Account or User Status is Inactive Request failed
403 4033702 Exceeds Transaction Amount Limit Request failed
403 4033719 Merchant Blacklisted (temporary disabled or missing authorization) Request failed
404 4043716 Partner Not Found (missing configuration) Request failed
403 4033706 Feature Not Allowed At This Time. In Maintenance Mode Request failed
400 4003702 Invalid Mandatory Field partnerReferenceNo Request failed
400 4003701 Invalid Field Format amount.value Request failed
400 4003702 Invalid Mandatory Field value Request failed
400 4003701 Invalid Field Format transactionDate: must be a valid date Request failed
400 4003702 Invalid Mandatory Field customerNumber Request failed
404 4043713 Invalid Amount Request failed
400 4003700 Bad Request currency: must be a valid value Request failed
403 4033723 Account Monthly Limit Exceed Request failed
409 4093700 Conflict Request failed
500 5003700 General Error (Internal server error) Request failed
500 5003702 Unknown Error Request failed

Notes:

  • Request success means Inquiry is all good.
  • Request failed means the Inquiry failed to be created in OVO system. Please do a new Inquiry again.

Customer Topup

This API will be used by Merchants to Initiate Topup

Diagram

Your request must contain the following information:

Request Body

Parameter Data Type Mandatory Description
partnerReferenceNo String M Transaction identifier on service consumer system.Need to be the same value from Customer Topup Inquiry Request
customerNumber String M Customers Account numberNeed to be the same value from Customer Topup Inquiry Request
customerName String O Customers Account name
>amount Object M
>>value String (ISO4217) M Net amount of the transactions. If it's IDR then the value includes 2 decimal digits. eg IDR 10,000,- will be placed with 10000.00 Need to be the same value from Customer Topup Inquiry Request
>>currency String M Currency Need to be the same value from Customer Topup Inquiry Request
>feeAmount Object M
>>value String (ISO4217) M Transaction feeNeed to be the same value from Customer Topup Inquiry Response
>>currency String M Currency Need to be the same value from Customer Topup Inquiry Response
transactionDate yyyyMMddhhmmss O Transaction date
sessionId strings O Invoice transactions ID
categoryId numeric O Category ID
notes strings O Transaction note
>additionalInfo Object O Additional information
>>senderInstitutionID String O Contains the ID of Sender Institution that is a unique identifier for institutions like Banks or Collecting Agents. This could be a Transfer Bank Code, Collecting Agent Code, or any other unique code assigned to these institutions. This will need to be mapped to Institution Name at OVO side, and the name will be shown in OVO User Transaction History Detail.
>>senderUniqueID String O Contains a unique identifier of the sender. If it is sensitive data it needs to be hashed.

Response Body

Parameter Data Type Mandatory Description
responseCode String M Response code
responseMessage String M Response description
referenceNo String C Transaction identifier on service provider system. Must be filled upon successful transactions 
partnerReferenceNo String M Transaction identifier on service consumer system
sessionId String O Transaction invoice ID
customerNumber String O Customers account number
>amount Object C
>>value String (ISO4217) M Net amount of the transactions. If it's IDR then the value includes 2 decimal digits. eg IDR 10,000,- will be placed with 10000.00
>>currency String M Currency
>additionalInfo Object O Additional information
>>responseInfo1 String O Top up response additional info 1
>>senderInstitutionID String O From the request
>>senderUniqueID String O From the request

Sample Request

{
    "partnerReferenceNo": "20220728000000001",
    "customerNumber": "080000000001",
    "amount": {
        "value": "100000.00",
        "currency": "IDR"
    },
    "feeAmount": {
        "value": "1000.00",
    "currency": "IDR"
    },
    "transactionDate": "2022-07-28T10:00:00+07:00",
    "additionalInfo": {
        "senderInstitutionID": "999",
        "senderUniqueID": "9999999999999999"
    }
}

Notes:

For partnerReferenceNo, in cases where it is not possible for the partner to ensure the same partnerReferenceNo between inquiry & top-up call, then just fill this independently. And let the OVO team know so that it is configured accordingly.

Sample Response

{
    "responseCode": "2003800",
    "responseMessage": "Request has been processed successfully",
    "referenceNo": "20220728000000001",
    "partnerReferenceNo": "20220728000000001",
    "customerNumber": "XXXXXXXX6594",
    "amount": {
        "value": "100000.00",
        "currency": "IDR"
    },
    "additionalInfo": {
        "senderInstitutionID": "999",
        "senderUniqueID": "9999999999999999"
    }
}

Response Code

HTTP Code Response Code Error Message (English) Remarks
200 2003800 Request has been processed successfully Request success
403 4033815 Transaction Not Permitted Request failed
403 4033802 Exceeds Transaction Amount Limit Request failed
403 4033823 Account Monthly Limit Exceed Request failed
403 4033805 Account or User status is abnormal Request failed
403 4033819 Merchant Blacklisted (temporary disabled or missing authorization) Request failed
404 4043816 Partner Not Found (missing configuration) Request failed
403 4033806 Feature Not Allowed At This Time. In Maintenance Mode Request failed
404 4043813 Invalid Amount Request failed
409 4093800 Conflict Request failed
400 4003800 Bad Request Request failed
400 4003802 Invalid Mandatory Field partnerReferenceNo Request failed
400 4003802 Invalid Mandatory Field customerNumber Request failed
400 4003802 Invalid Mandatory Field value Request failed
400 4003801 Invalid Field Format transactionDate: must be a valid date Request failed
400 4003800 Bad Request currency: must be a valid value Request failed
500 5003800 General Error (Internal server error) Request failed
500 5003802 Unknown Error UNKNOWN, please do check inquiry top up status
504 - Timeout / Gateway timeout UNKNOWN, please do check inquiry top up status
5xx - Any unknown 5xx error UNKNOWN, please do check inquiry top up status

Notes:

  • Request success means topup is all good
  • Request failed means topup failed. Please create a new inquiry and topup from the start.
  • UNKNOWN means we are not sure of topup transaction state. Please do check status instead of creating new one using the corresponding transaction ID to know the transaction status

Customer Topup Inquiry Status

This API will be used by Merchants to get top-up status for a specific top-up reference no

Diagram

Request Body

Parameter Data Type Mandatory Description
originalPartneReferenceNo String  Transaction identifier/reference generated by partners.
originalReferenceNo String  Transaction identifier/reference generated by PJP AIS Apart from Banks
originalExternaIID String  Original X EXTERNAL ID from top-up requests.
serviceCode  String  To identify which transactions that need to be checked. (38)
>additionalInfo  Object  O Additional information

Response Body

Parameter Data Type Mandatory Description
responseCode  String  Response code
responseMesage String  Response description
originalPartnerReferenceNo String  Transaction identifier/reference generated by partners.
originalReferenceNo String  Transaction identifier/reference generated by PJP AIS Apart from Banks.
originalExternalID String  Original X EXTERNAL ID from top-up requests.
serviceCode  String  To identify which transactions that need to be checked.
>amount  Object  C
>>value  String (ISO4217) Net amount of the transactions. If it's IDR then the value includes 2 decimal digits. eg IDR 10,000,- will be placed with 10000.00 
>>currency  String  Currency 
latestTransactionStatus  String  00 - Success 01 - Initiated 02 - Paying 03 - Pending 04 - Refunded 05 - Canceled 06 - Failed 07 - Not foundThis field is valid when responseCode are: 200xx00, 404xx01
  • Success: 00
  • Failed: 04, 05, 06, 07 
    • 07 means failed only if responseCode: 404xx01. Can ignore 07 for other responseCode.
    • Please create new trx when failed
  • Payment Not Found: 01 (payment call has not been received, and payment retry is possible)
  • Pending/Suspect: 02, 03 (can retry the check status after next few minutes)
transactionStatusDesc String  Description status transactions
>additionalInfo  Object  O Additional information

Sample Request

{
    "originalReferenceNo": "20220728000000001",
    "serviceCode": "38"
}

Sample Response

{
    "responseCode": "2003900",
    "responseMessage": "Request has been processed successfully",
    "originalPartnerReferenceNo": "",
    "originalReferenceNo": "20220728000000001",
    "serviceCode": "38",
    "amount": {
         "value": "100000.00",
         "currency": "IDR"
    },
    "latestTransactionStatus": "00",
    "transactionStatusDesc": "Success"
}

Error Code

HTTP Code Response Code Error Message (English) Remarks
200 2003900 Request has been processed successfully Request success.See “latestTransactionStatus” response for further action.
404 4043901 Transaction Not Found Please create new trx
409 4093900 Conflict (X-EXTERNAL-ID) Request failed
404 4043916 Partner Not Found (missing configuration) Request failed
400 4003900 Bad Request Request failed
400 4003902 Invalid Mandatory Field Request failed
500 5003900 General Error (Internal server error) UNKNOWN, please retry
500 5003902 Unknown Error UNKNOWN, please retry
504 - Timeout / Gateway timeout UNKNOWN, please retry
5xx - Any unknown error UNKNOWN, please retry

Notes:

  • Request success means partner get the response so partner knows what is the latestTransactionStatus of the transaction. See “latestTransactionStatus” response section for further action.
  • Request failed means there’s an issue in the request. Please make sure partner sends a correct request header/body and retry the check status again. If partner is already sending correct request and still has this issue, please contact OVO.
  • Please create new trx, means there’s an issue in corresponding transaction and payment has not been received. Please create a new transaction with new trx ID.
  • UNKNOWN means request doesn’t have proper response due to some unknown issues in OVO. Please retry the check status until partner gets proper response other than 5xx
    • Retry check status can be done few times (up to partner) in an interval time but if after few times partner still not receive proper response, please keep the status as it is. Usually it can be settled in the reconciliation process, or partner can ask the latest status to OVO via communication channel.

Customer Topup Check Balance Deposit

This API will be used by Merchants to get top-up status for a specific top-up reference no

Request Body

Method URL
GET /cashin/snap/v2.0/merchant/account/balance

Response Body

Parameter Data Type Mandatory Description
>accountList  Object 
accountType String  Merchant account type
codeStr String  Account Code
>amount  Object 
currency String  Currency
value  Big Decimal  Account Balance Information

Sample Response

Code Response Notes
200
{
    "accountList": [
        {
            "accountType": "OVO Cash",
            "codeStr": "CASH",
            "amount": {
                "value": "468288578",
                "currency": "IDR"
            }
        }
    ]
}
Success
400
{
    "error": {
        "code": "OV00058",
        "message": "You are not allowed to do this action"
    },
    "accountList": []
}
Failed
400
{
    "error": {
        "code": "OV00086",
        "message": "Partner not found"
    },
    "accountList": []
}
Failed
400
{
    "error": {
        "code": "OV00098",
        "message": "Merchant id for this partner doesn't exist"
    }
    "accountList": []
}
Failed
400
{
    "error": {
        "code": "OV00009",
        "message": "Account Not Found"
    },
    "accountList": []
}
Failed
400
{
    "error": {
        "code": "OV00061",
        "message": "Something unexpected happen in server"
    },
    "accountList": []
}
Failed
401
{
   "data": {
      "actionables": [
         {
           "pinWebviewURL": "https://webview.byte-stack.net/cellblockui/v2/paymentPin",
           "qParams": {
             "action": "regeneratePayment",
             "client-id": "snapinternalcimb"
           },
           "token": "PaymentToken"
         }
      ],
      "error": {
         "code": "OV00502",
         "message": "Unauthorized access"
      }
   },
   "responseCode": "4013701",
   "responseMessage": "Invalid Token:Anda Tidak Memiliki Akses"
}
Failed
401
{
   "responseCode": "4013700",
   "responseMessage": "Unauthorized. No Interface Definition: Timestamp."
}
Failed
401
{
   "responseCode": "4013700",
   "responseMessage": "Unauthorized. Signature Failed: AGW-002-05."
}
Failed