SNAP Open API v1.0.7
Overview of OpenAPI for Linking and Payments
Host :
https://app.byte-stack.net
https://apigw.ovo.id
Method | Endpoint | Usage |
---|---|---|
POST | /OVOSNAP/v2.0/oauth/account/registration-account-binding |
Account Binding |
POST | /OVOSNAP/v2.0/access-token/b2b2c |
Access Token Request |
POST | /OVOSNAP/v2.0/access-token/b2b2c |
Access Token Refresh |
POST | /OVOSNAP/v1.0/access-token/b2b |
Generate System Token (B2B) |
GET | /user/v2/account/lookup |
Lookup Phone No |
POST | /OVOSNAP/v2.0/oauth/account/registration-account-unbinding |
Account Unbinding |
POST | /OVOSNAP/v2.0/balance-inquiry |
Balance Inquiry |
POST | /OVOSNAP/v2.0/debit/payment-host-to-host |
Direct Debit |
POST | /OVOSNAP/v2.0/debit/refund |
Direct Debit Refund |
POST | /OVOSNAP/v2.0/debit/status |
Direct Debit Inquiry Status |
All the APIs mentioned are to follow the standard header format mentioned here, unless stated otherwise
Parameter | 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. Bearer eyJraWQiOi...JzcIiwiY) |
No Need For
|
Bearer gp9HjjEj813Y9JGoqwOeOPWbnt4CUpvIJbU1mMU4a11MNDZ7Sg5u9a |
X-CLIENT-KEY |
Client's client_id. Use this for
|
Conditional | oamerchantg |
X-PARTNER-ID |
Client's client_id. Use this for
|
Conditional | oamerchantg |
X-TIMESTAMP | Client's current local time in yyyyMMddTHH:mm:ss.SSSTZD format | Mandatory | |
X-SIGNATURE |
|
Mandatory | Please check Signature Section |
X-DEVICE-ID | Device identification on which the API services are currently being accessed by the end-user (customer) - String (400) | Mandatory | |
X-EXTERNAL-ID | Unique ID to avoid duplication. ID reset for every 24 hours. String (36) | Mandatory |
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.
/*
How to use:
- Configure the client ID via request headers with key as x-client-key.
- Configure the private key via environment variable with key as {clientID}_private_key.
- Script will generate / replace environment variables listed in the Output section.
Output (Environment variables):
- gen_signature -> add to X-Signature headers
- gen_timestamp -> add to X-Timestamp headers
*/
eval( pm.environment.get('pmlib_code') )
var moment = require('moment')
const clientID = pm.environment.get("clientID")
var privateKey = pm.environment.get("_private_key")
const timestamp = moment().format("YYYY-MM-DDThh:mm:ss.SSSZ")
console.log("clientID: " + clientID)
console.log("privateKey: " + privateKey)
console.log("timestamp: " + timestamp)
const msg = `${clientID}|${timestamp}` // Please use this for Token Request
const msg = `${method}:${fullPath}:${finalBody}:${timestamp}` // Please use this for Account Binding
console.log("msg: " + msg)
var CryptoJS = require("crypto-js")
const hashedMsg = CryptoJS.SHA256(msg)
console.log("hashedMsg: " + hashedMsg)
var sig = new pmlib.rs.KJUR.crypto.Signature({"alg": "SHA256withRSA"})
privateKey = pmlib.rs.KEYUTIL.getKey(privateKey)
sig.init(privateKey)
const hash = sig.signString(msg)
console.log("hash: " + hash)
pm.environment.set("gen_timestamp", timestamp)
pm.environment.set("gen_signature", hash)
Signature is used to verify that your open API service request is not altered by attackers.
SHA-512 HMAC is used to generate the signature with your Client Secret as the key.
/*
How to use:
- Configure the client ID via request headers with key as x-partner-id.
- Configure the access token via request headers with key as Authorization and format of value as Bearer {access_token}.
- Configure the secret key via environment variable with key as {clientID}_secret_key.
- Script will generate / replace environment variables listed in the Output section.
Output (Environment variables):
- gen_signature -> add to X-Signature headers
- gen_timestamp -> add to X-Timestamp headers
*/
eval( pm.globals.get('pmlib_code') )
var moment = require('moment')
var CryptoJS = require("crypto-js")
const clientID = pm.environment.get('clientID');
const accessToken = pm.environment.get("access_token");
const timestamp = moment().format("YYYY-MM-DDThh:mm:ss.SSSZ")
const body = pm.request.body.raw
const fullPath = pm.request.url.getPathWithQuery()
const method = pm.request.method
var secret_key = pm.environment.get("_secret_key")
console.log("clientID: " + clientID)
console.log("accessToken: " + accessToken)
console.log("timestamp: " + timestamp)
console.log("body: " + body)
console.log("fullPath: " + fullPath)
console.log("method: " + method)
console.log("secret_key: " + secret_key)
const hashedBody = CryptoJS.SHA256(body).toString()
const finalBody = hashedBody.toLowerCase()
console.log("hashedBody: " + hashedBody)
console.log("finalBody: " + finalBody)
const msg = `${method}:${fullPath}:${accessToken}:${finalBody}:${timestamp}`
console.log("msg: " + msg)
const hash = CryptoJS.HmacSHA512(msg, secret_key).toString()
console.log("hash: " + hash)
pm.environment.set("gen_timestamp", timestamp)
pm.environment.set("gen_signature", hash)
pm.environment.set("api", "/OVOSNAP/v2.0/debit/payment-host-to-host'")
Signature is used to verify that your open API service request is not altered by attackers.
JAVA
Signature Calculation Util
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.HmacUtils;
public class Calculator {
public static String get(String appId, String key, String time, String method,String url ,String body) {
final String urlParams = method+" "+url;
final String secretKey = DigestUtils.sha256Hex(key);
final String baseMessage = createBaseMessage(appId, time, urlParams, body.getBytes());
String signature = HmacUtils.hmacSha256Hex(secretKey, baseMessage);
return signature;
}
public static String createBaseMessage(String appId, String time, String urlParam, byte[] byteBody) {
final String payloadBase64 = Base64.encodeBase64URLSafeString(byteBody);
return appId + time + urlParam + payloadBase64;
}
}
Usage
Calculator.get("ovo_partner", "7b3e13a21764563721fbeef29c3b3102", "1523111100000", "POST", "/user/v1/oauth/otp/generate", "body");
Expected : "ddc622679740a983258d8783d5e721849d89919940fe63ca185a8f06471f00a6"
Notes:
Here is the list of error codes that can be returned.
HTTP Code | Error Code | Error Message (Indonesian) |
---|---|---|
401 | 4017300 | Unauthorized. [HMAC mismatch] |
400 | 4007301 | Invalid timestamp format [X-TIMESTAMP] |
504 | 5047300 | Timeout |
400 | 4007300 | Invalid field format [clientId/clientSecret/grant_type] |
For Openssl version 1.x.x
openssl genrsa -out private.pem 4096
For Openssl version 3.x.x and above
openssl genrsa -traditional -out private.pem 4096
openssl rsa -in private.pem -RSAPublicKey_out -out public.pem
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))
}
This is the process of linking a 3rd party service to use OVO as a service. The process involves necessary user authorizations to authorize client actions on behalf of the user.
This is to check the status of a user by sending their phone number to the below mentioned endpoint. OVO requires an Indonesian phone number to activate the account.
/user/v2/account/lookup
Status | Definition |
---|---|
ACTIVE | User is an active OVO user ( Linkage Allowed) |
ACTIVE_NO_PIN | User is an active OVO user with NO PIN ( Linkage Allowed) |
CAN_REGISTER | User does not have OVO account ( Linkage Not Allowed) |
BLOCKED_MSISDN | User is blocked by OVO ( Linkage not allowed) |
LINKED | User is already linked with the partner. |
signature |
Key | Value |
---|---|
phone | <phone> |
ResponseCode | Response | Remarks |
---|---|---|
200 |
{
|
ACTIVE
ACTIVE_NO_PIN CAN_REGISTER BLOCKED_MSISDN LINKED |
400/500 |
{
|
The following is the specific error code for this API
HTTP Status | Error Code | Error Description |
---|---|---|
5xx | OV00001 | Internal Server Error |
4xx | OV00002 | Bad/Incomplete Request |
4xx | OV00013 | Client ID not valid/enabled |
5xx | OV00061 | Something unexpected happen in server |
4xx | OV00001 | HMAC signature does not match |
This API will be used by Merchants to bind OVO Merchant ID and OVO User Phone No
/{version}/registration-account-binding
Parameter | Data Type | Mandatory | Description | |
---|---|---|---|---|
phoneNo | String | M | Phone Number of the user
Format: 08xxxxxx |
|
additionalInfo | Object | |||
Object | O | User email registered on merchant’s platform. For example [email protected] | ||
merchantId | String | M | Unique merchant identifier | |
device | Object | O | Device related info | |
ID | String | O | ||
Manufacturer | String | O | ||
Model | String | O | ||
OS | String | O | ||
OsVersion | String | O | ||
position | Object | O | Position related info | |
Lat | String | O | ||
Lon | String | O |
Parameter | Data Type | Description | ||
---|---|---|---|---|
responseCode | String | Internal OVO error/success codes | ||
responseMessage | String | Response Description | ||
referenceNo | String | Reference number to refer to the webview session, sending upon succesful binding. (refid) | ||
linkageToken | String | Token to access webview | ||
redirectUrl | String | URL to redirect user to OVO webview | ||
additionalInfo | Object | |||
account | Object | |||
accountStatus | String | Current account status for the user’s OVO account | ||
qparams | Object | |||
action | String | Action type | ||
authType | String | Type of authentication | ||
client-id | String | Registered client-id of the client | ||
refid | String | Reference id to refer the webview session | ||
phoneNumber | String | Phone number of the user |
curl --location 'https://app.byte-stack.net/OVOSNAP/v2.0/oauth/account/registration-account-binding' \
--header 'X-Signature: 82ab1b8c03ff99ce07a70794dd3d2dd327d852887392069f1049cba3a5e5a1aeb77fbf556555a63614dbabeef490cf36c79248d9dbad07c0ca7f71a8d1f147f3f5ceb512286de0e7e239de2bc25afde011c12d75c373f60fe31bfcab7393959e77ebc2dd3340a5f3b489c4d6734f2a9bcf61004e0dbdc1b3cab24f5fa4f3faca96ddcff5b874c3c3c1ad5d4d247d005dd39d4c7b0214cc6e372c0cbd2173b8fcaaff10c5dbadad2225e602403430a8aa5a2d896dd2f97da58c8c58dcfc0fbd5cffd2d5f4bcd31f67a5b0917362c0e62b2e726443c7ff8179186b6b9d0410dea40ad57df4a05cae6ee798887715a890971e0b6d9945b50af21b040281cbe713e7ff12966342fd3b24858c7b0a4359f48f9a7a8b48aa81d82c86797b46e911415452e1195dea090f7bf3372aeb8bd1c23ed91b965692398584a666310a0652c48903e0d6552a1201425c3164a4552dbfb29b150c46b94a1815911b69d60e45f40ee6366d420acaaed6d3c9f5592a844671f990d134ae267816831440124ad397e27523e1706c70e6443fa611be61d466d63b3b63f8d0af58d99d18a99ab8125cdb3e0654417be99cf0096c0f60cc7b42aa13db7d2a1694cdf5352c04189efa0ef1ddb2497357ea5eea485565ca67e5f5be8ced72af38fa8a2d58b845a6903f884a508e68f1b72045dcf36f2b380cb075f30e7cb12c37ded77d698af9531046e42a' \
--header 'X-Timestamp: 2024-06-10T11:07:22.401+05:30' \
--header 'X-Partner-ID: oamerchantam' \
--header 'Content-Type: application/json' \
--header 'X-EXTERNAL-ID: QAPayx-00003' \
--header 'Cookie: __cf_bm=ouBU3xu2XIakFHAkG_pQ7y9IGtPBSHapV_pajNQAC6A-1717997824-1.0.1.1-887CEo_bqNPKHjDVG9loagCFDvIdJL7wmCjArWo_2_heGT2yBmsR.93mDuGfOTayxB2Rk_8SO9DlQMkITC_rkw' \
--data '{
"phoneNo": "080069696333",
"merchantId": "117661"
}'
{
"responseCode": "2000700",
"responseMessage": "Success",
"referenceNo": "2e24c3d4-d8c5-4de6-8bb1-520a2bf403f1",
"linkageToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik9UQTFaalkzTlRZelltTTFOREkyWkRnNVpqTmpPRFpqTkdOalkySXlNMlkiLCJyYW5kb20iOiJNVEl4TVRRd05qVSIsInZlcnNpb24iOjF9.UaWK4z8ZO8cUkI19AnHEAMaej0kkLqNfj7LWZs33CAA",
"redirectUrl": "https://webview.byte-stack.net/cellblockui/partner/activation",
"additionalInfo": {
"account": {
"accountStatus": "LINKED"
},
"qParams": {
"action": "otpLinkage",
"authType": "2FA",
"client-id": "oamerchantam",
"phoneNumber": "08006969****",
"refId": "2e24c3d4-d8c5-4de6-8bb1-520a2bf403f1",
"skipTnc": "true"
}
}
}
In case the user is blocked at our end (due to multiple wrong OTP/Pin attempts or other reasons) the error like below would be returned with HTTP code 403
{
"data": null,
"responseCode": "4030707",
"responseMessage": "Card/User Blocked:Akun ini terkunci sementara. Silakan tunggu 1 jam lagi."
}
Partners can also add additional state parameter in the query parameters while opening the webview.
https://webview.byte-stack.net/cellblockui/partner/activation?authType=2FA&submissionType=redirect&destination=https://piswebblank.pis&action=otpLinkage&phoneNumber=08000080****&refId=a8afa060-c13b-4502-a19a-d53eefb435f7&client-id=oamerchantam&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik5qSmtOamt4TkdSak9XSTFOR0ZsTmpreU5ETTRZamczWldZMU0ySXdaR00iLCJyYW5kb20iOiJOalF3TmpRM09BIiwidmVyc2lvbiI6MX0.qZyXr5zVpiEtGqYbdPISHD9ylr8bOALEibR1jQTeAcg
The query param value must follow Account Binding API Response
After successful PIN verification, following query parameter will be received after redirection to partner domain
Parameter | Data Type | Description |
---|---|---|
authCode | String | AuthCode sent to partner after successful user verification |
state | String | State parameter(optional) as received from partner in query parameters while opening the OVO webview |
displayMessage | String | Message presenting the final status of verification process |
displayHeader | String | Can Ignore this |
retryAttemptsLeft | String | Number of remaining PIN challenge maximum attemp |
errorCode | String | Error Code |
enable_redirection | Boolean | Redirection toggle to do redirection to callback URL |
After successful PIN verification, the following query parameter will be received post redirection. The parameters relevant to the partner are mentioned in the table above.
response={
"displayMsg":"SUCCESS",
"displayHeader":"",
"retryAttemptsLeft":4,
"authCode":"1IdrZkhTTvi2AZ59Zz9usA",
"state":"4b3fd750012a585c46aec444",
"errorCode":"",
"enable_redirection":false
}
In case error redirection is enabled for a partner, the following query parameter will be received after redirection to partner domain.
errorResponse={"code":"OV00006","state":"1234"}
errorResponse={
"code":"OV00006",
"state":"1234"
}
Notes : This required a special config from OVO and not all merchants used this, meaning this only returned for clients who expect a redirection in case of certain failures in webview
Error Code | Error Description |
---|---|
OV00521 | Too many wrong OTP |
OV00003 | Exceeds attempt limit, Not allowed request otp for 30min |
OV00529 | Your account is fully blocked |
OV00527 | Your account is blocked. Please try after 60min |
Error redirection is the capability to redirect user back to client domain from OVO webview for terminating error use cases; by default error redirection is disabled meaning user will be redirected to client domain only after success user verification in OVO webview
HTTP status | Error Code | Status | Message |
---|---|---|---|
500 | 5000701 | FAILED | Unknown Error |
400 | 4000700 | FAILED | Bad Request |
400 | 4000700 | FAILED | Invalid Merchant |
404 | 4040716 | FAILED | Partner not found |
404 | 4040701 | FAILED | Transaction Not found |
429 | 4290700 | FAILED | Too Many Requests |
403 | 4030705 | FAILED | Do Not Honor |
500 | 5000702 | FAILED | Unknown Error |
500 | 5000700 | FAILED | General Error |
401 | 4010700 | FAILED | Unauthorized |
401 | 4010701 | FAILED | Invalid Token |
404 | 4040712 | FAILED | Invalid Bill/Virtual Account |
400 | 4000701 | FAILED | Invalid Field Format |
403 | 4030715 | FAILED | Transaction Not Permitted |
404 | 4040715 | FAILED | Invalid OTP |
403 | 4030718 | FAILED | Inactive Card/Account/Customer |
405 | 4050700 | FAILED | Requested Function Is Not Supported |
405 | 4050701 | FAILED | Requested Operation Is Not Allowed |
403 | 4030707 | FAILED | Card/User Blocked |
This API call is required to be made using the linkageToken received from Account Creation/Binding Response. Use the token in Authorisation header
/{version}/access-token/b2b2c
Parameter | Data Type | Mandatory | Description |
---|---|---|---|
authCode | String(256) | C | AuthCode received after PIN challenge(Conditional) |
refreshToken | String(512) | C |
Refresh token to get a new accessToken where the User doesn't need to provide the consent again. mandatory if grantType = refresh_token. Refresh Token should be less than access token validity and will be managed by the PJP's application to generate a new access_token(Conditional) |
grantType | String | M | Apply token request key type, can be authorization_code or refresh_token |
Parameter | Data Type | Description | |
---|---|---|---|
responseCode | String | Internal OVO error/success codes | |
responseMessage | String | Response Description | |
accessToken | String(2048) | A string representing an authorization issued to the client that used to access protected resources | |
accessTokenExpiryTime | String | Datetime of token expiration. Format: ISO8601 | |
tokenType | String | The access token type provides the client with the information required to successfully utilize the access token to make a 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 | |
refreshToken | String | A random string that can be used by specific client to get a refreshed accessToken to prolong the access to the User's resources. | |
refreshTokenExpiryTime | String | Time when the refreshToken will be expired. Refresh Token should be less than access token validity and will be managed by the PJP's application to generate a new access_token | |
additionalInfo | Object | ||
ilp | String | Ovo identifier for a particular linkage. Can be later used for unlink, troubleshooting/debugging issues, sharing information, translating to OVO channel user ID etc with ovo. |
curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v2.0/access-token/b2b2c' \
--header 'X-Signature: 2b87b4157d56b798d954ab6bc1a02d48b9825d5a85237757da9463842d4c2abe4790c1bcaf9239f577d08e7ec4fed2ca4e76f50abf8f5d04be36308983ae78ca760954fd43421cdd9715bd47ccadab16441c7d030378fd78c011fc6b9cdf3c9f1cd6687b550b682c895b86e9c7556e5d8381a69d32327cbeaf8e7600da224bdd5d4d0d96c43a9f2dc62cc9fe6fc8a61c60f9d1193b73a4bba0750e5b218ed94eedd8fa1362f7cffdf48c64be0f6f353c3ef909390fa8df2cd6d0b2ecb93394ed4c80ed660a3909b375c9c04f56bacf064a151246bb333dc51c91f1f1a160c2ed3f5da3d91e5c3ed5f2f9cc9e31af840074611ba44d7c9d1604d0520e6813757a0745f86e6a230975b0b648c9a1fef26d8ef33518723b12b574423546dc871ad37f6ac7349a79db98385f01dcc3ffe810ddd6cc59a104695cf48ee1a7d28721fad3f6c825b837a4b64fbc49c65a318e415ca50cd5310341fc382a31a5ed1cc880b3753c3b6920fb5fe2befe5cd906552ce649e803aac0f22041b2d59983325bb425438dd4b9bdb4a0ab21820b65ef23f1a648819bbe60e0479e1e92f3f26b699aba973298fd7bf386d90473fc2b5905904e38b5e5b3c979c2d7e9cc7ccd89b6d529cde09f128e718ce52cb7097268de0ae37020f212af23c8156d4df29e94f0ae607ddf65d85a9ff14dff0c6aa06021304271ab2c888ee1524f79e586b9af4723' \
--header 'X-Timestamp: 2022-08-04T10:25:13.511+07:00' \
--header 'X-Client-Key: oamerchantam' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik16WmlaRFUxTmpGbVkyUTJORFkyWldKa1pEZzNNVEJoWmpKa01EZGlORFkiLCJyYW5kb20iOiJOalF3TlRZeU5nIiwidmVyc2lvbiI6MX0.njOfzmHQ9-vk93NmqF1xFBU82hfy2XBYMoNODrQY-ss' \
--header 'Content-Type: application/json' \
--header 'X-EXTERNAL-ID: binding1-1' \
--data-raw '{
"grantType" : "authorization_code",
"authCode" : "Z269GYi7QYWCWOiTcLeC3A"
}'
{
"responseCode": "2007400",
"responseMessage": "Success",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik5HSmpPRE5oWkRBeVlUQTROREpqT1dFell6Z3paVGxqTXpoak16azBZVFkiLCJyYW5kb20iOiJOalF3TlRZMU9BIiwidmVyc2lvbiI6MX0.8gQ0LLb7THupNBoJz0Utqc6DH7XpDVSZYiAs__8nIaI",
"tokenType": "Bearer",
"accessTokenExpiryTime": "2023-08-04T03:25:14Z",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik1UTmhPVGsyWXpVNU4yWXhOR0psWkdJME5XWXpZemxsTVRnMVpqSTJOVFUiLCJyYW5kb20iOiJOalF3TlRZMU53IiwidmVyc2lvbiI6MX0.GKM-C2dLlpNUN4JqkvbmB11nt4oiaZ7XG_KcvfJoVAI",
"refreshTokenExpiryTime": "",
“additionalInfo” : {
“Ilp” : “ovo.user.testMerchantBISnap.ADABMQAwATACMwIyAAEACQAFAgAHAg”
}
}
ILP id is a unique identifier for OVO to identify the user for a particular partner linkage. Partners need to store the ILP id at their end which could be later used to share information relating to a user
As per BI requirements, the token expiry for the access token received in this API should be 15 days
Partner can refresh the expired tokens, call the API B2B2C Access Token with grantType = refresh_token
For responseCode: OV00502 or 401XX00, partner are required to regenerate access tokens
{
"data": {
"actionables": [
{
"pinWebviewURL": "https://webview.byte-stack.net/cellblockui/v2/paymentPin",
"qParams": {
"action": "regeneratePayment",
"client-id": "testMerchantBISnap"
},
"token": "PaymentToken"
}
],
"error": {
"code": "OV00502",
"message": "Unauthorized access"
}
},
"responseCode": "401XX01",
"responseMessage": "General unauthorized error (No Interface Def, API is Invalid, Oauth Failed, Verify Client Secret File, Client Forbidden Access API, Unknown Client, Key not Found)"
}
If data.error.code == OV00502 or 401XX01, we expect clients to refresh the old tokens.
This reactive approach is to be applied for refreshing all
types of tokens
For expired token condition, please use below flow
HTTP status | Error Code | Status | Message |
---|---|---|---|
500 | 5007401 | FAILED | Unknown Error |
400 | 4007400 | FAILED | Bad Request |
404 | 4047416 | FAILED | Partner not found |
404 | 4047401 | FAILED | Transaction Not found |
429 | 4297400 | FAILED | Too Many Requests |
403 | 4037405 | FAILED | Do Not Honor |
500 | 5007402 | FAILED | Unknown Error |
500 | 5007400 | FAILED | General Error |
401 | 4017400 | FAILED | Unauthorized |
401 | 4017401 | FAILED | Invalid Token |
404 | 4047412 | FAILED | Invalid Bill/Virtual Account |
400 | 4007401 | FAILED | Invalid Field Format |
403 | 4037415 | FAILED | Transaction Not Permitted |
404 | 4047415 | FAILED | Invalid OTP |
403 | 4037418 | FAILED | Inactive Card/Account/Customer |
405 | 4057400 | FAILED | Requested Function Is Not Supported |
405 | 4057401 | FAILED | Requested Operation Is Not Allowed |
403 | 4037407 | FAILED | Card/User Blocked |
This API will be used by Merchants to trigger Refund API
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
/{version}/access-token/b2b
Parameter | Data Type | Mandatory | Description |
---|---|---|---|
grant_type |
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 |
Parameter | Data Type | Description |
---|---|---|
responseCode | string | Response Code |
responseMessage | string | Response Message |
accessToken | string | A string representing an authorization issued to the client that used to access protected resources |
tokenType | string | The access token type provides the client with the information required to successfully
utilize the access token to make a protected resource request (along with type-specific
attributes) Token Type Value:
|
expiresIn | string | Time in seconds |
curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v1.0/access-token/b2b' \
--header 'x-client-key: oamerchantam' \
--header 'Content-Type: application/json' \
--header 'X-Signature: d34174d14808b8ea3c7f631ba18c22ef83beb040d080e972292920c5ea0a255220f8d53623da5e2bacbf2cda96e1c57eae7e2ad25328afaefcaa01b8176492455b2498fedfc9a0e76e0d759a0ffb6d2f8cb7f2ee0f63f86510520b145e9459057c794e4fcd5e540cc3e2d68a95c58396690356da2f7e6fc68e4a7085a7ea3ef00f4c1a8fac1c1513bf5294f23145ee3f680251fc41a953058603c547a5097872d5d38f64e1cbdf1899e54df6ab27cc571a1f3b45e403a0e6a1dfcffd346440270f93943ffc4acf49a890157605855591e66f0a60c8a7d39576a2764794c4e26c67e2e50f67fb279808a0579b311b3e2a55b872aa04a82e266fed91b3984fd9336eee161775324f0a138320505123bea26fcc7465fb1cc6a47cf81308a8540eb1e2fa3e18e575befea76f244bdc4b65baf28e19b46bde6d4fe6fdf0323400a27e9761e6f86650dbdf6273b535abc15ad0f3f7c441bd9a434c173e474f13b578eab93329001b7baed333fa39ee4f2c5445d4d71f076c58251198959cc4ce84bf65e4fd8457e993040d29809f94a8c5d4bc3f975491c0849a8f92f8cfb4876805353decbf90050993b43c0ef72e25cb43f6bb0769ca029c711b5e872f12f972fd42606eebe613ed34774128b1f5bf607df5c8e8a2cd0a820bd5c406c1ada033537b06248636750c3746bdebe0fca7ba5d3cff8c70de6fc0392a030b52ce152b74a6' \
--header 'x-Timestamp: 2022-08-04T10:29:29.190+07:00' \
--header 'X-EXTERNAL-ID: binding1-1' \
--data-raw '{
"grantType" : "client_credentials"
}'
curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v1.0/access-token/b2b' \
--header 'x-client-key: oamerchantam' \
--header 'Content-Type: application/json' \
--header 'X-Signature: d34174d14808b8ea3c7f631ba18c22ef83beb040d080e972292920c5ea0a255220f8d53623da5e2bacbf2cda96e1c57eae7e2ad25328afaefcaa01b8176492455b2498fedfc9a0e76e0d759a0ffb6d2f8cb7f2ee0f63f86510520b145e9459057c794e4fcd5e540cc3e2d68a95c58396690356da2f7e6fc68e4a7085a7ea3ef00f4c1a8fac1c1513bf5294f23145ee3f680251fc41a953058603c547a5097872d5d38f64e1cbdf1899e54df6ab27cc571a1f3b45e403a0e6a1dfcffd346440270f93943ffc4acf49a890157605855591e66f0a60c8a7d39576a2764794c4e26c67e2e50f67fb279808a0579b311b3e2a55b872aa04a82e266fed91b3984fd9336eee161775324f0a138320505123bea26fcc7465fb1cc6a47cf81308a8540eb1e2fa3e18e575befea76f244bdc4b65baf28e19b46bde6d4fe6fdf0323400a27e9761e6f86650dbdf6273b535abc15ad0f3f7c441bd9a434c173e474f13b578eab93329001b7baed333fa39ee4f2c5445d4d71f076c58251198959cc4ce84bf65e4fd8457e993040d29809f94a8c5d4bc3f975491c0849a8f92f8cfb4876805353decbf90050993b43c0ef72e25cb43f6bb0769ca029c711b5e872f12f972fd42606eebe613ed34774128b1f5bf607df5c8e8a2cd0a820bd5c406c1ada033537b06248636750c3746bdebe0fca7ba5d3cff8c70de6fc0392a030b52ce152b74a6' \
--header 'x-Timestamp: 2022-08-04T10:29:29.190+07:00' \
--data-raw '{
"grantType" : "client_credentials"
}'
{
"responseCode": "2007300",
"responseMessage": "Success",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik56UTJPV0pqWWpSallXVmhOREEyTkdFM1lXSmhPVFF3T1dZek9EZG1PVFkiLCJyYW5kb20iOiJOalF3TlRZMk1nIiwidmVyc2lvbiI6MX0.JunB-LHxNnkGsSKb6P72iMaztZFuRyTuxGCZevrC6cE",
"tokenType": "Bearer",
"expireIn": "300",
}
There are two ways in which a partner can refresh the expired tokens
HTTP status | Error Code | Status | Message |
---|---|---|---|
500 | 5007301 | FAILED | Unknown Error |
400 | 4007300 | FAILED | Bad Request |
404 | 4047316 | FAILED | Partner not found |
404 | 4047301 | FAILED | Transaction Not found |
429 | 4297300 | FAILED | Too Many Requests |
403 | 4037305 | FAILED | Do Not Honor |
500 | 5007302 | FAILED | Unknown Error |
500 | 5007300 | FAILED | General Error |
401 | 4017300 | FAILED | Unauthorized |
401 | 4017301 | FAILED | Invalid Token |
404 | 4047312 | FAILED | Invalid Bill/Virtual Account |
400 | 4007301 | FAILED | Invalid Field Format |
403 | 4037315 | FAILED | Transaction Not Permitted |
404 | 4047315 | FAILED | Invalid OTP |
403 | 4037318 | FAILED | Inactive Card/Account/Customer |
405 | 4057300 | FAILED | Requested Function Is Not Supported |
405 | 4057301 | FAILED | Requested Operation Is Not Allowed |
403 | 4037307 | FAILED | Card/User Blocked |
To allow a user to unlink/unbind an existing binding with OVO, client needs to engage user with necessary authorisations before asking OVO to unlink the account.
To allow a user to unlink/unbind an existing binding with OVO, the client needs to engage the user with necessary authorizations before asking OVO to unlink the account.
If multiple bindings are allowed for a client, all the bindings for a user through such a client (for instance GooglePlay) will be unbinded.
/{version}/registration-account-unbinding
Parameter | Data Type | Mandatory | Description |
---|---|---|---|
partnerReferenceNo | String | M | phone Number |
merchantId | String | M | Unique merchant identifier |
Parameter | Data Type | Description |
---|---|---|
responseCode | String | Internal OVO error/success codes |
responseMessage | String | Response Description |
referenceNo | String | Reference number to refere to the webview session, sending upon successful binding. (refid) |
unlinkResult | String | The status of the unbinding request |
curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v2.0/oauth/account/registration-account-unbinding' \
--header 'X-Signature: a9f58d3d78bd829cee92af8a65f07ae7a74f79498212204dcf4281bd9c26a08368036c83fab508930cb44355209ab3c4eb84cdd5c02e747dbbce00226b874d38' \
--header 'X-Timestamp: 2022-08-04T07:43:27.257+07:00' \
--header 'X-Partner-ID: oamerchantam' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik9XVTFZMlpoTVdJelpHTXdOREprTXprNVpqbGtNbVZpTm1VMk1XSXpNVFEiLCJyYW5kb20iOiJOalF3TmpRNE1RIiwidmVyc2lvbiI6MX0.AQfIXOJ39MEGdXWkiNjX7TL_64sTNOgWrtWqjceLWUg' \
--header 'Content-Type: application/json' \
--header 'X-EXTERNAL-ID: binding1-1' \
--data-raw '{
"partnerReferenceNo" : "080000809420",
"merchantId" : "118631"
}'
{
"responseCode": "2000900",
"responseMessage": "Success",
"referenceNo": "796a2b38-1bcd-46d4-8c43-aadec3c6778c",
"unlinkResult": "Success",
"additionalInfo": null
}
HTTP status | Error Code | Status | Message |
---|---|---|---|
500 | 5000901 | FAILED | Unknown Error |
400 | 4000900 | FAILED | Bad Request |
404 | 4040916 | FAILED | Partner not found |
404 | 4040901 | FAILED | Transaction Not found |
429 | 4290900 | FAILED | Too Many Requests |
403 | 4030905 | FAILED | Do Not Honor |
500 | 5000902 | FAILED | Unknown Error |
500 | 5000900 | FAILED | General Error |
401 | 4010900 | FAILED | Unauthorized |
401 | 4010901 | FAILED | Invalid Token |
404 | 4040912 | FAILED | Invalid Bill/Virtual Account |
400 | 4000901 | FAILED | Invalid Field Format |
403 | 4030915 | FAILED | Transaction Not Permitted |
404 | 4040915 | FAILED | Invalid OTP |
403 | 4030918 | FAILED | Inactive Card/Account/Customer |
405 | 4050900 | FAILED | Requested Function Is Not Supported |
405 | 4050901 | FAILED | Requested Operation Is Not Allowed |
403 | 4030907 | FAILED | Card/User Blocked |
This API will be used by Merchants to get OVO Balance for a specific user
Parameter | Data Type | Mandatory | Description | |
---|---|---|---|---|
partnerReferenceNo | String | O | Transaction identifier on service consumer system | |
bankCardToken | String | O | Card token for payment | |
accountNo | String | M | OVO Phone No | |
[balanceType] | Array of Object | O | ["CASH","POINTS"] Please use capital letter | |
additionalInfo | Object | O | ||
deviceId | String | O | ||
channel | String | O |
Parameter | Data Type | Mandatory | Description | ||
---|---|---|---|---|---|
responseCode | String | M | Response code | ||
responseMessage | String | M | Response description | ||
referenceNo | String | O | Transaction identifier on service provider system. Must be filled upon successful transaction | ||
partnerReferenceNo | String | O | Transaction identifier on service consumer system | ||
accountNo | String | O | Registered account number | ||
name | String | O | Customer account name | ||
[accountInfos] | Array of Object | ||||
balanceType | String | O | Account type name | ||
amount | Object | O | |||
value | String (ISO4217) | M | Net amount of the transaction. If it’s IDR then the value includes 2 decimal digits. e.g. IDR 10.000,- will be placed with 10000.00 | ||
currency | String | M | Currency | ||
floatAmount | Object | O | |||
value | String (ISO4217) | C | Amount of deposit that is not effective yet (due to holiday, etc.). If it’s IDR then the value includes 2 decimal digits. e.g. IDR 50.000,- will be placed with 50000.00 Must be filled if the floatAmout data exist | ||
currency | String | C | Currency Must be filled if the flagAmout data exist | ||
holdAmount | Object | O | |||
value | String (ISO4217) | C | Hold amount that cannot be used. If it’s IDR then the value includes 2 decimal digits. e.g.
IDR 20.000,- will be placed with 20000.00 Must be filled if the floatAmout data exist |
||
currency | String | C | Currency Must be filled if the floatAmout data exist | ||
availableBalance | Object | O | |||
value | String (ISO4217) | C | Account balance that can be used for financial transaction Must be filled if the availableBalance data exist |
||
currency | String | C | Currency Must be filled if the availableBalance data exist | ||
ledgerBalance | Object | O | |||
value | String (ISO4217) | C | Account balance at the beginning of each day Must be filled if the ledgerBalance data exist |
||
currency | String | C | Currency | ||
currentMultilateralLimit | Object | O | |||
value | String (ISO4217) | C | Credit limit of the account / plafon Must be filled if the currentMultilateralLimit data exist |
||
currency | String | C | Currency Must be filled if the currentMultilateralLimit data exist | ||
registrationStatusCode | String | O | Customer registration status | ||
status | String | O | Account Status 1 = Active Account 2 = Closed Account 4 = New Account 6 = Restricted Account 7 = Frozen Account 9 = Dormant Account |
||
additionalInfo | Object | O | |||
deviceId | String | O | |||
channel | String | O |
curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v2.0/balance-inquiry' \
--header 'X-PARTNER-ID: oamerchantam' \
--header 'X-TIMESTAMP: 2022-08-04T05:10:39.126+07:00' \
--header 'X-SIGNATURE: a833036113fcd913f3f304a0cc6548ffa8277a611a469db8404e2942fb2165175f545fd253ec06b8ebfe1cfc2ad5c24229ec0bd85024b63cdcf2aec598afbfa7' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik5HSmpPRE5oWkRBeVlUQTROREpqT1dFell6Z3paVGxqTXpoak16azBZVFkiLCJyYW5kb20iOiJOalF3TlRZMU9BIiwidmVyc2lvbiI6MX0.8gQ0LLb7THupNBoJz0Utqc6DH7XpDVSZYiAs__8nIaI' \
--header 'Content-Type: application/json' \
--header 'X-EXTERNAL-ID: balance4' \
--data-raw '{
"partnerReferenceNo":"ccc",
"bankCardToken":"6d7963617264746f6b656e",
"accountNo":"080000809420",
"balanceType":["Cash","Points"],
"additionalInfo": {
"deviceId":"12345679237",
"channel":"mobilephone"
}
}'
{
"responseCode": "2001100",
"responseMessage": "Request has been processed successfully",
"accountInfos": [
{
"balanceType": "CASH",
"amount": {
"value": "19992334.00",
"currency": "IDR"
},
"floatAmount": {
"value": "19992334.00",
"currency": "IDR"
},
"holdAmount": {
"value": "19992334.00",
"currency": "IDR"
},
"availableBalance": {
"value": "19992334.00",
"currency": "IDR"
},
"ledgerBalance": {
"value": "19992334.00",
"currency": "IDR"
},
"currentMultilateralLimit": {
"value": "19992334.00",
"currency": "IDR"
}
},
{
"balanceType": "POINTS",
"amount": {
"value": "1000000.00",
"currency": "IDR"
},
"floatAmount": {
"value": "1000000.00",
"currency": "IDR"
},
"holdAmount": {
"value": "1000000.00",
"currency": "IDR"
},
"availableBalance": {
"value": "1000000.00",
"currency": "IDR"
},
"ledgerBalance": {
"value": "1000000.00",
"currency": "IDR"
},
"currentMultilateralLimit": {
"value": "1000000.00",
"currency": "IDR"
}
}
]
}
HTTP status | Error code | Status | Message |
---|---|---|---|
200 | 2001100 | SUCCESS | Request has been processed successfully |
400 | 4001100 | FAILED | Bad Request. the accountNo allowed max length is 16 |
400 | 4001100 | FAILED | Bad Request. the accountNo field is required |
400 | 4001100 | FAILED | Bad Request. invalid format accountNo field |
409 | 4091100 | FAILED | Conflict |
This endpoint is able to give you SingleUseToken .The token returned will depend on the <auth_code> given from the PIN WebView after successful PIN verification.
This will be used only for Direct Debit API
Method | URL |
---|---|
POST | /user/v1/oauth/token |
Key |
---|
Authorization: Bearer <access_token> |
Key | Value |
---|---|
grantType | authorization_code |
code | <auth_code> |
Status | Response |
---|---|
grantType |
{ |
code |
{ |
This API will be used by Merchants to initiate Payment to OVO with Cash and Points
Parameter | Data Type | Mandatory | Description | ||
---|---|---|---|---|---|
partnerReferenceNo | String | M | Transaction identifier on service consumer system | ||
bankCardToken | String | O | |||
merchantId | String | M | Merchant identifier that is unique per each merchant | ||
terminalId | String | O | Terminal ID | ||
journeyId | String | O | Merchant ID | ||
subMerchantId | String | O | Sub-merchant ID | ||
amount | Object | O | |||
value | String | M | Net amount of the transaction. If it’s IDR then the value includes 2 decimal digits. e.g. IDR 10.000,- will be placed with 10000.00 | ||
currency | String | M | Currency | ||
urlParam | Object | O | |||
url | String | M | The URL | ||
type | String | M | URL Type PAY_RETURN/PAY_NOTIFY | ||
isDeeplink | String | M | Whether the URL is a deep link URL or not Y/N | ||
externalStoreId | String | O | Store ID to indicate to which store this payment belongs to. | ||
validUpTo | String | O | The time when the payment will be automatically expired. ISO 8601 | ||
pointOfInitiation | String | O | used for getting more info regarding source of request of the user | ||
feeType | String | O | to whom the fee will be charged 1. OUR Fee is charged to the sender (default) 2. BEN Fee is charged to the recipient 3. SHA|1000 Fee is shared between sender and recipient, with sender is charged Rp 1.000,00 and the recipient will be charged the rest | ||
disabledPayMethods | String | O | Payment method(s) that cannot be used for this payment | ||
payOptionDetails | Object | M | |||
payMethod | String | M | [“CASH”,”POINTS”] Please use capital letter | ||
payOption | String | M | Payment option which shows the provider of this payment e.g. CREDIT_CARD_VISA | ||
transAmount | Object | M | |||
value | String | M | Transaction amount that will be paid using this paymentmethod If it’s IDR then value includes 2 decimal digits. e.g. IDR 10.000,- will be placed with 10000.00 | ||
currency | String | M | Currency | ||
feeAmount | Object | O | |||
value | String (ISO 4217 ) | C | Fee amount that will be paid using this payment method If it’s IDR then value includes 2 decimal digits. e.g. IDR 10.000,- will be placed with 10000.00Must be filled if the feeAmount data exist | ||
currency | String | C | CurrencyMust be filled if the feeAmount data exist | ||
cardToken | String | O | Card token used for this payment | ||
chargeToken | String | M | Use default value “OVO” | ||
merchantToken | String | O | Merchant token used for this payment | ||
additionalInfo | Object | O | Additional information | ||
deviceId | String | O | |||
channel | String | O | |||
additionalInfo | Object | O | Additional information | ||
deviceId | String | O | |||
channel | String | O | |||
subTransactionType | String | C | Conditional for Auto Debit Feature. Please refer to below section for Auto Debit | ||
issuingMerchantName | String | O | |||
notes | String | O |
The feature is used to allow Direct Debit API to be used without PIN process, e.g. in the recurring payment process. In the request payload, to allow this process, the field “subTransactionType” must be present. Currently supported field value:
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 transaction | |
partnerReferenceNo | String | O | Transaction identifier on service consumer system | |
appRedirectUrl | String | O | Returns an URL scheme to the PJP AIS payment page in native app. | |
webRedirectUrl | String | O | Returns a universal link to PJP AIS payment page. This link is recommended when the Client is unable to implement a check for whether PJP AIS app is installed on the user’s device before redirect. | |
additionalInfo | Object | O | Additional information | |
deviceId | String | O | ||
channel | String | O |
curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v2.0/debit/payment-host-to-host'' \
--header 'X-PARTNER-ID: oamerchantam' \
--header 'X-TIMESTAMP: 2022-08-04T05:35:32.848+07:00' \
--header 'X-SIGNATURE: b31edf113da4e6c7a0266e3417a05fad979b6a4ad803b89e9708ffe07447e317fa321b508f4d312a5beac1c40123bcf279ba4e5ab4506e38db6f42c919ef250d' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik1HWmpNemd3TVRaaU5UZzNOREJsTTJFNU5ERXhaVEl3TmpaaE1UTXhaV0kiLCJyYW5kb20iOiJOalF3TmpRd05nIiwidmVyc2lvbiI6MX0.tq0rURYiLDfl5JfeNP4vGHjzR56UUaZ6u-GAZAj4H7s' \
--header 'Content-Type: application/json' \
--header 'X-EXTERNAL-ID: payment6' \
--data-raw '{
"partnerReferenceNo": "dwiyogapoint12",
"chargeToken": "OVO",
"merchantId": "117661",
"amount": {
"value": "10000.00",
"currency": "IDR"
},
"payOptionDetails":
[{
"payMethod": "CASH",
"payOption": "OVO",
"transAmount": {
"value": "10000.00",
"currency": "IDR"
}
}
]
}'
{
"responseCode": "2025400",
"responseMessage": "Transaction still on process",
"referenceNo": "",
"partnerReferenceNo": "dwiyogapoint12",
"webRedirectUrl": "https://webview.byte-stack.net/cellblockui/v2/paymentPin",
"additionalInfo": {
"clientTxnId": "dwiyogapoint12",
"action": "payment",
"client-id": "oamerchantam"
}
}
https://piswebblank.pis/?response={"displayMsg":"SUCCESS","displayHeader":"","retryAttemptsLeft":4,"authCode":"lGG73abGSdaz6BEGw8VHnw","errorCode":"","enable_redirection":false}
{
"responseCode": "2005400",
"responseMessage": "Request has been processed successfully",
"referenceNo": "dwiyogapoint12",
"partnerReferenceNo": "dwiyogapoint12",
"webRedirectUrl": ""
}
HTTP status | Error code | Status | Message |
---|---|---|---|
200 | 2005400 | SUCCESS | Request has been processed successfully |
400 | 4005400 | FAILED | Bad Request. the partnerReferenceNo field is required |
500 | 5005400 | FAILED | General Error. request not valid |
400 | 4005400 | FAILED | Bad Request. the merchantId field is required |
400 | 4005400 | FAILED | Bad Request. the amount field is required |
400 | 4005400 | FAILED | Bad Request. can't convert {amount} to decimal: too many .s |
400 | 4005400 | FAILED | Bad Request. request not valid |
500 | 5005400 | PENDING | General Error. Transaction pending |
403 | 4035414 | FAILED | Insufficient Funds |
403 | 4035401 | FAILED | Feature Not Allowed |
400 | 4005400 | FAILED | Bad Request. the payOption is not valid |
409 | 4095400 | FAILED | Conflict |
This API will be used by Merchants to initiate Payment Refund to OVO
Parameter | Data Type | Mandatory | Description | ||
---|---|---|---|---|---|
merchantId | String | O | Merchant identifier that is unique per each merchant | ||
subMerchantId | String | O | Sub-merchant ID | ||
originalPartnerReferenceNo | String | M | Original transaction identifier on service consumer system | ||
originalReferenceNo | String | O | Original transaction identifier on service provider system | ||
originalExternalId | String | O | Original Customer Reference Number | ||
partnerRefundNo | String | M | Reference Number from PJP AIS for the refund | ||
refundAmount | Object | M | |||
value | String (ISO 4217) | M | Fee amount that will be paid using this payment method If it’s IDR then value includes 2 decimal digits. e.g. IDR 10.000,- will be placed with 10000.00 | ||
currency | String | M | Currency | ||
externalStoreId | String | O | External Store ID | ||
reason | String | O | Refund reason. | ||
additionalInfo | Object | M | JSON object which contains array of the detail refund amount (per Source of Fund)
Example:
{
|
||
accType | String | M | Current supported values: "CASH" for cash refund, "POINTS" for point refund | ||
amt | String | M | Detail amount | ||
value | Numeric | M | Refund amount | ||
currency | String | M | Currently supported value: "IDR" |
Parameter | Data Type | Mandatory | Description | |||
---|---|---|---|---|---|---|
responseCode | String | M | Response Code | |||
responseMessage | String | M | Response Description | |||
originalPartnerReferenceNo | String | O | Transaction identifier on service provider system. Must be filled upon successful transaction | |||
originalReferenceNo | String | C | Transaction identifier on service consumer system | |||
originalExternalId | String | O | Original Customer Reference Number | |||
partnerTrxId | String | O | Partner Transaction ID | |||
refundNo | String | M | Reference Number | |||
partnerRefundNo | String | M | Reference Number from PJP AIS for the refund. | |||
refundAmount | Object | M | ||||
value | String (ISO 4217) | M | Fee amount that will be paid using this payment method. If it’s IDR then value includes 2 decimal digits. e.g. IDR 10.000,- will be placed with 10000.00 Must be filled if the refundAmount data exist | |||
currency | String | M | Currency Must be filled if the refundAmount data exist | |||
refundTime | String | M | Refund time. ISO 8601 | |||
additionalInfo | Object | M | JSON object which contains array of the detail refund amount (per Source of Fund) Example:
{
|
|||
sof | Array | M | Array which contains accType and amt | |||
accType | String | M | Current supported values:
|
|||
amt | Object | M | Detail amount | |||
value | Numeric | M | Refund amount | |||
currency | String | M | Currently supported value: "IDR" |
curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v2.0/debit/refund' \
--header 'X-PARTNER-ID: oamerchantam' \
--header 'X-TIMESTAMP: 2022-08-04T05:36:46.090+07:00' \
--header 'X-SIGNATURE: f8c6cadafdd480412a7cf6886068703d39e6e8f34ebfde87c3e7fcd5382a603be1abc8a0695f5c7d74e157d67fe9f205dc832aef73738d1c7f2b2795137dfda1' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik5HSmpPRE5oWkRBeVlUQTROREpqT1dFell6Z3paVGxqTXpoak16azBZVFkiLCJyYW5kb20iOiJOalF3TlRZMU9BIiwidmVyc2lvbiI6MX0.8gQ0LLb7THupNBoJz0Utqc6DH7XpDVSZYiAs__8nIaI' \
--header 'Content-Type: application/json' \
--header 'X-EXTERNAL-ID: dwiyoga12' \
--data-raw '{
"originalPartnerReferenceNo": "dwiyogapoint12",
"partnerRefundNo": "refund-dwiyogapoint12",
"refundAmount": {
"value": "1000.00",
"currency": "IDR"
},
"additionalInfo": {
"sof": [
{
"accType": "CASH",
"amt": {
"value": "1000.00",
"currency": "IDR"
}
}
]
}
}'
{
"responseCode": "2005800",
"responseMessage": "Request has been processed successfully",
"originalReferenceNo": "dwiyogapoint12",
"partnerRefundNo": "refund-dwiyogapoint12",
"refundAmount": {
"value": "1000.00",
"currency": "IDR"
},
"refundTime": "2022-08-04T10:36:46Z"
}
HTTP status | Error code | Status | Message |
---|---|---|---|
200 | 2005800 | SUCCESS | Request has been processed successfully |
400 | 4005800 | FAILED | Bad Request. the originalPartnerReferenceNo field is required |
404 | 4045801 | FAILED | Transaction Not Found. ledger system resource not found |
400 | 4005800 | FAILED | Bad Request. the partnerRefundNo field is required |
400 | 4005800 | FAILED | Bad Request. the refundAmount.amount field is required |
400 | 4005800 | FAILED | Bad Request. can't convert {amount} to decimal: too many .s |
400 | 4005800 | FAILED | Bad Request. request not valid |
404 | 4045811 | FAILED | Customer Not Match |
400 | 4005802 | FAILED | Exceeds Transaction Amount Limit |
400 | 4005815 | FAILED | Transaction Not Permitted |
403 | 4035802 | FAILED | Exceeds Transaction Amount Limit. the amount requested exceeds refundable amount |
409 | 4095800 | FAILED | Conflict |
This API will be used by Merchants to get the specific transaction Status
Parameter | Data Type | Mandatory | Description | |
---|---|---|---|---|
originalPartnerReferenceNo | String | M | Original transaction identifier on service consumer system | |
originalReferenceNo | String | O | Original transaction identifier on service provider system | |
originalExternalId | String | O | Original ExternalID on header message | |
serviceCode | String | M |
Transaction type indicator (service code of the original transaction request)
Code 54 (Direct Debit Service Code) If it is not 54, return 400 |
|
transactionDate | String | O | transaction date : ISO 8601 | |
amount | Object | O | ||
value | String (ISO 4217) | M | Net amount of the transaction. If it’s IDR then the value includes 2 decimal digits. e.g. IDR 10.000,- will be placed with 10000.00 | |
currency | String | M | Currency | |
merchantId | String | O | Merchant identifier that is unique per each merchant | |
subMerchantId | String | O | Sub-merchant ID | |
externalStoreId | String | O | External Store ID for merchant | |
additionalInfo | Object | O | ||
deviceId | String | O | ||
channel | String | O |
Parameter | Data Type | Mandatory | Description | ||
---|---|---|---|---|---|
responseCode | String | M | Response Code | ||
responseMessage | String | M | Response Description | ||
originalPartnerReferenceNo | String | O | Transaction identifier on service provider system. Must be filled upon successful transaction | ||
originalReferenceNo | String | C | Transaction identifier on service consumer system | ||
originalExternalId | String | O | Original ExternalID on header message | ||
serviceCode | String | M | Transaction type indicator (service code of the original transaction request) | ||
latestTransactionStatus | String | M | 00 - Success 01 - Initiated 02 - Paying 03 - Pending 04 - Refunded 05 - Canceled 06 - Failed 07 - Not found | ||
transactionStatusDesc | String | O | Description status transaction | ||
originalResponseCode | String | M | Response code | ||
originalResponseMessage | String | O | Response description | ||
sessionId | String | O | Transaction invoice ID | ||
requestID | String | O | Transaction invoice ID | ||
refundHistory | Array of Object | C | If refund data exist, then the field must be filled | ||
refundNo | String | C | Transaction Identifier on Service Provider System | ||
partnerReferenceNo | String | C | Reference Number from PJP AIS for the refund | ||
refundAmount | Object | ||||
value | String (ISO 4217) | C | Net amount of the refund | ||
currency | String | C | Currency | ||
refundStatus | String | C | 00 - Success 03 - Pending 06 - Failed | ||
refundDate | String | C | (ISO 8601) transaction date : dd-MMyyyy (Mandatory ) HH:mm:ss (Optional) | ||
reason | String | C | Refund reason | ||
paidTime | String | C | transaction date : ISO 8601 | ||
additionalInfo | Object | O | |||
deviceId | String | O | |||
channel | String | O |
curl --location --request POST 'https://app.byte-stack.net/OVOSNAP/v2.0/debit/status' \
--header 'X-PARTNER-ID: oamerchantam' \
--header 'X-TIMESTAMP: 2022-08-04T05:37:27.114+07:00' \
--header 'X-SIGNATURE: 65028204f6097a11d8d8456f1a10d4a27730a9c45c1f547f5547495bc93ee9071a048e70b64cf6e42b040eaa9ad18fbd1d666ba84051e70965359a305206f1a1' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik5HSmpPRE5oWkRBeVlUQTROREpqT1dFell6Z3paVGxqTXpoak16azBZVFkiLCJyYW5kb20iOiJOalF3TlRZMU9BIiwidmVyc2lvbiI6MX0.8gQ0LLb7THupNBoJz0Utqc6DH7XpDVSZYiAs__8nIaI' \
--header 'Content-Type: application/json' \
--data-raw '{
"originalPartnerReferenceNo": "dwiyogapoint12",
"serviceCode": "54"
}'
{
"responseCode": "2005500",
"responseMessage": "Request has been processed successfully",
"originalPartnerReferenceNo": "dwiyogapoint12",
"serviceCode": "54",
"latestTransactionStatus": "00",
"refundHistory": [
{
"refundNo": "refund-dwiyogapoint12",
"refundAmount": {
"value": "1000.00",
"currency": "IDR"
},
"refundStatus": "00",
"refundDate": "2022-08-04T10:36:46Z"
}
],
"transAmount": {
"value": "10000.00",
"currency": "IDR"
}
}
HTTP status | Error code | Status | Message |
---|---|---|---|
200 | 2005500 | SUCCESS | Request has been processed successfully |
400 | 4005500 | FAILED | Bad Request. the originalPartnerReferenceNo field is required |
400 | 4005500 | FAILED | Bad Request. the serviceCode field is required |
400 | 4005500 | FAILED | Bad Request. the given serviceCode was invalid |
404 | 4045501 | FAILED | Transaction Not Found |
409 | 4095500 | FAILED | Conflict |
This API will be used to convert Payment Token generated from API Linkage V1 to V2/SNAP Access Token and Refresh Token without any relinking activity needed from the users
Method | URL |
---|---|
POST | /v1/oauth/token/linkageMigrate?grantType=authorization_code |
Key |
---|
Authorization: Bearer <Linkage V1 Payment Token> |
curl --location --request POST '{{HOSTNAME}}/v1/oauth/token/linkageMigrate?grantType=authorization_code \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6Ik56SmhOV0ZpWXpFNFlqRTVORFUzTUdGbE1EUXdOalpqTlRFM05qVXpPREEiLCJyYW5kb20iOiJOVGMyTXpZeU5BIiwidmVyc2lvbiI6MX0.M5WeZ6LyJNJnhnBCiGEfHvvgG1vTE343N1Mp7epSWhg' \
--header 'time: 1652077563147' \
--header 'signature: f5faff203eb6f1c5743dbc56232a4898fa271245df11185df5766bf7c87fe1cb' \
--header 'client-id: test'
{
"tokens": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6IllUWmxZVFExTkRBd04yVXlORFF4T0RoaVpHTTROall3TmpaaE5tWXhZVEEiLCJyYW5kb20iOiJOak13TURrM05BIiwidmVyc2lvbiI6MX0.gTirMlNU1udcSkhptLvH7LLToZZuJPmmAW2aptlLVYA",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlaGFzaCI6IlpqZ3pNakZoTkdFd1kyUmhOR0V3WkdGaU5EWXdNRFU1TnpkaFl6ZGpNamMiLCJyYW5kb20iOiJOak13TURrM013IiwidmVyc2lvbiI6MX0.68PPrwQTC-iMQiPqTdJ3W9aRnnHxUv0pKHaVCPvPjYg",
"expires_in": 604800,
"token_type": "Bearer"
},
"ilp": "ovo.user.testMerchantBISnap.ADABMQAwATACMwIyAAEACQAFAgAHAg"
}
ILP id is a unique identifier for OVO to identify the user for a particular partner linkage. Partners need to store the ILP id at their end which could be later used to share information relating to a user