Android Connect

Integrate PortOne Payment Gateway with your Android app using the Android Connect SDK, enabling secure and efficient payment processing. This guide will walk you through the steps needed to integrate the SDK and prepare your app for checkout.

The PortOne Android SDK simplifies the integration of the PortOne Payment Gateway into your Android app, offering a secure, reliable, and efficient way to accept payments. With this SDK, you can seamlessly connect your app to a variety of payment channels, providing a smooth payment experience for users.


Video Tutorial

The following two video tutorials provide a detailed guide on integrating the PortOne Android SDK with your application to enable seamless payment processing:


Sample App

Check the sample app to integrate on GitHub


Prerequisites

  • Create an account on PortOne:
    Before proceeding with the integration, ensure you have created an account on PortOne to access their services and functionalities.
  • Enable Payment Channels and Methods:
    Customize and enable the specific payment channels and methods that align with your business requirements and preferences.
  • Android application for the integration:
    You will need an existing Android application in which you intend to integrate the PortOne Android SDK for payment processing capabilities.
  • authKey to access the SDK:
    Obtain an authorization key (authKey) from the PortOne Team, as it is required to securely access and utilize the features of the PortOne SDK in your Android application. authKey will be issued by the PortOne Team by sending us email on this [email protected]

Integration

Steps to integrate your Android application with PortOne Android SDK.

  1. Install PortOne Android SDK and Authorise it.
  2. Set the Intent Filters in the Manifests
  3. Set Intent Receivers for Payment Status
  4. Setup to Obtain JWT Token from the Server
  5. Generate a Signature Hash for Payload

1. Install PortOne Android SDK and Authorise it.

To begin, add the PortOne Android SDK to your project by adding the dependency to your build.gradle file.

  • In the build.gradle (:app) file, add:

implementation 'com.github.iamport-intl:chaipay-android-native-sdk:V3.0.37'
  • Next, add the authKey to your gradle.properties file:.
authKey= XXXXXXXXXXXXXXXXXXXXXX
  • Authorize the SDK by adding the provided authKey to your gradle.properties file, which you can request by emailing the PortOne team at [email protected].

Then, configure your build.gradle (:Project) or settings.gradle to reference the key:

  1. Build.gradle (:Project) Setup:

    repositories {
            maven { url '<https://maven.google.com/>' }
            maven{
                url '<https://jitpack.io>'
                credentials { username authKey }
            }
        }
    
  2. Settings.gradle Setup:

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            maven {
                url 'https://maven.google.com/'
            }
            maven {
                url 'https://jitpack.io'
                credentials { username authKey } // Add your generated authKey here
            }
        }
    }
    

Include the necessary code in your build.gradle () file, specifying the project dependency and Kotlin version.

buildscript {
    ext.kotlin_version = "1.5.10" // Specify the Kotlin version here
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.2" // Add the Android Gradle plugin version
        
        // Add any other dependencies needed for your project setup
    }
}

2. Set the Intent Filters in the Manifests

  • Next, you need to add intent filters in your AndroidManifest.xml to handle payment status updates and navigate users back to your app after payment.

    <activity android:name=".CheckoutActivity">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data
                android:scheme="portone"
                android:host="checkout" />
        </intent-filter>
    </activity>
    
    

This setup ensures your app can handle the redirection URL after payment completion. For detailed instructions, refer to the deep linking guide here.

3. Set Intent Receivers for Payment Status:

You’ll need to handle the payment status response using the onActivityResult method. Here’s an example:.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (resultCode == RESULT_CODE && data != null) {
            when (requestCode) {
                PAYOUT_REQUEST_CODE, PAYMENT_STATUS_REQUEST_CODE -> {
                    val paymentStatus: PaymentDto.PaymentStatus? =
                        (data.getSerializableExtra(PAYMENT_STATUS)
                            ?: "Empty") as PaymentDto.PaymentStatus
                }

            }

        }
    }

This method receives the payment status and updates the user interface with the payment result.

4. Have a setup to get JWT token from the server

The PortOne SDK requires a JWT token for authentication. You need to implement a server-side process to generate this token and retrieve it in your Android app.

Steps:

  1. Implement server logic to generate a JWT token.
  2. In your Android app, fetch the token to process the checkout.

The process for generating a JWT token can be found in detail here

5. Generate Signture Hash

To generate a signature hash, create it on the server side using a secret key that will be included in the payload. This ensures secure processing of transactions.

Steps:

  1. Implement server logic to generate a signature hash.
  2. In your Android app, fetch the signature hash to process the checkout.

The process for generating a Signature Hash can be found in detail here


Android Connect

Android SDK Variant V2 empowers merchants with advanced business logic functionalities, focusing solely on core processing capabilities without the inclusion of pre-built user interface components. This design choice offers flexibility to merchants, allowing them the freedom to craft and customize their own unique checkout interface tailored to their brand aesthetics and user experience preferences.

The Android SDK V2 provides a detailed breakdown of key functionalities essential for merchants to enhance their payment processing capabilities efficiently. Each point represents a specific feature or action that can be easily referenced for seamless integration and utilization within merchant applications.

Overview:

  1. Fetch Enabled Payment Methods and Channels
  2. Checkout Using a New Credit/Debit Card
  3. Checkout Using Saved Credit/Debit Cards
    1. Retrieve Saved Cards Using Phone Number
      1. Fetch OTP for Authentication
      2. Fetch Saved Cards Using Phone Number and OTP
    2. Retrieve Saved Cards Using Auth Token
  4. Checkout Using Non-Tokenized Methods
    1. Instalments
    2. Direct Bank Transfer
  5. Merchant Centric Card Vault
    1. Create a Customer
    2. Save a Card for Customer
    3. Fetch a List of Cards for a Customer
    4. Delete Card for a Customer
  6. PreAuth and Capture
  7. Failover Routing

The subsequent methods are elaborated upon extensively, including their practical applications:

  1. This method is utilized to retrieve the available payment channels and methods that can be used for the checkout process.

    ParametersData Type
    portoneKeyStringmandatory
    subMerchantKeyStringOptional
    currencyStringmandatory
    portOne.getPaymentMethods(
                    portoneKey = portoneKey,
                    subMerchantKey = subMerchantKey,
                    currency = currency,
                    object :
                        ApiCallbackInterface<PaymentDto.PaymentMethodResponse> {
                        override fun onSucceed(response: PaymentDto.PaymentMethodResponse) {
                            LoggerUtil.info("Successful")
                        }
    
                        override fun onFailure(
                            errorCode: Int?,
                            status: String?,
                            errorMessage: String,
                            throwable: Throwable
                        ) {
                            LoggerUtil.info("Failed")
                        }
                    })
    
  2. This method is employed to initiate the checkout process with a new card, allowing for payment from a card that has not been saved previously.

    portOne.checkoutUsingNewCard(
                    paymentDetails = PaymentDto.CheckoutUsingTokenizationRequest(),
                    newCard = PaymentDto.NewCard(),
                    token = jwtToken,
                )
    

    jwtToken

    The jwtToken serves as the authentication mechanism in the API, generated following the guidelines outlined in the integration documentation.

    PaymentDto.NewCard

    ParametersData Type
    cardNumberStringmandatory
    cardTypeStringmandatory
    cardholderNameStringmandatory
    serviceCodeStringmandatory
    expiryYearStringmandatory
    expiryMonthStringmandatory
    environmentStringmandatory
    portoneKeyStringmandatory

    PaymentDto.CheckoutUsingTokenizationRequest

    ParametersData Type
    portoneKeyStringmandatory
    paymentChannelStringmandatory
    paymentMethodStringmandatory
    merchantOrderIdStringmandatory
    amountDoublemandatory
    currencyStringmandatory
    billingDetailsCheckoutPaymentDto.BillingDetailsOptional
    shippingDetailsCheckoutPaymentDto.ShippingDetailsOptional
    orderDetailsList<CheckoutPaymentDto.OrderDetail>Optional
    successUrlStringmandatory
    failureUrlStringmandatory
    environmentStringmandatory
    signatureHashStringmandatory
    redirectUrlStringmandatory
    descriptionStringOptional
    sourceStringmandatory
    transactionTypeStringmandatory
  3. To make a payment using saved cards for the first time, the following method facilitates fetching the cards linked to the phone number. However, to access the cards, OTP authentication is necessary, requiring a two-step process.

    1. Retrieving the saved cards for the initial occurrence.

      1. This method is employed to send an OTP to the registered phone number.

        ParametersData Type
        phoneNoStringmandatory
        portOne.getOTP(phoneNo = phoneNo, object :
                        ApiCallbackInterface<PaymentDto.OtpResponse> {
                        override fun onSucceed(response: PaymentDto.OtpResponse) {
                            LoggerUtil.info("Successful")
                        }
        
                        override fun onFailure(
                            errorCode: Int?,
                            status: String?,
                            errorMessage: String,
                            throwable: Throwable
                        ) {
                            LoggerUtil.info("Failed")
                        }
                    })
        
      2. Upon completing the previous step, an OTP will be sent to the phone number, serving as the input for subsequent authentication.

        Data Type
        StringOptional
        Stringmandatory
        Stringmandatory
        Stringmandatory
        portOne.getSavedCards(
                        token = null,
                        portoneKey = portoneKey,
                        phoneNo = phoneNo,
                        otp = otp,
                        object :
                            ApiCallbackInterface<PaymentDto.CreditCardDetailsResponse> {
                            override fun onSucceed(response: PaymentDto.CreditCardDetailsResponse) {
                                LoggerUtil.info("Successful")
                            }
        
                            override fun onFailure(
                                errorCode: Int?,
                                status: String?,
                                errorMessage: String,
                                throwable: Throwable
                            ) {
                                LoggerUtil.info("Failed")
                            }
                        })
        
    2. Retrieving the saved cards repeatedly.
      Following the initial retrieval of saved cards, a token is obtained in the response, offering an alternative method for authentication. This token can be utilised in place of the OTP, providing convenience for subsequent card retrievals.

      ParametersData Type
      tokenStringmandatory
      portoneKeyStringmandatory
      phoneNoStringmandatory
      otpStringOptional
      portOne.getSavedCards(
                      token = token,
                      portoneKey = portoneKey,
                      phoneNo = phoneNo,
                      otp = null,
                      object :
                          ApiCallbackInterface<PaymentDto.CreditCardDetailsResponse> {
                          override fun onSucceed(response: PaymentDto.CreditCardDetailsResponse) {
                              LoggerUtil.info("Successful")
                          }
      
                          override fun onFailure(
                              errorCode: Int?,
                              status: String?,
                              errorMessage: String,
                              throwable: Throwable
                          ) {
                              LoggerUtil.info("Failed")
                          }
                      })
      
  4. The following method is utilized for processing non-tokenized payment methods, where tokenization is not required for transaction completion. This method facilitates streamlined checkout experiences for payment methods that do not necessitate the tokenization process.

    portOne.checkoutWithoutTokenization(
                    request = PaymentDto.CheckoutWithoutTokenizationReques()
                )
    

    For the non-tokenized flow, it is necessary to pass an object containing the checkout request details to facilitate the payment process without requiring tokenization.

    PaymentDto.CheckoutWithoutTokenizationRequest

    ParametersData Type
    portoneKeyStringmandatory
    paymentChannelStringmandatory
    paymentMethodStringmandatory
    merchantOrderIdStringmandatory
    amountDoublemandatory
    currencyStringmandatory
    billingDetailsCheckoutPaymentDto.BillingDetailsOptional
    shippingDetailsCheckoutPaymentDto.ShippingDetailsOptional
    orderDetailsList<CheckoutPaymentDto.OrderDetail>Optional
    successUrlStringmandatory
    failureUrlStringmandatory
    environmentStringmandatory
    signatureHashStringmandatory
    redirectUrlStringmandatory
    descriptionStringOptional
    sourceStringmandatory
    transactionTypeStringmandatory

    The parameters mentioned above are standard for non-tokenized checkout methods. However, additional parameters and steps are required for specific methods such as Direct Bank Transfer and Instalments.


    These methods also fall under the non-tokenized flow but necessitate extra parameters and steps in the request process.

    1. Direct Bank Transfer
    2. Instalments

    These methods, while classified under non-tokenized flows, entail supplementary parameters and steps in the request, as outlined in detail below.

    Direct Bank Transfer

    To process a Direct Bank Transfer checkout, the following steps should be followed:

    1. Fetch Direct Bank Transfer Details:
      Utilise the following method that only requires the portoneKey as input to retrieve the details pertaining to Direct Bank Transfer.

      portOne.getDBTDetails(portoneKey = portoneKey,
                      object : ApiCallbackInterface<PaymentDto.DBTResponse> {
                          override fun onSucceed(response: PaymentDto.DBTResponse) {
                              LoggerUtil.info("Successful")
                          }      
      
                          override fun onFailure(
                              errorCode: Int?,
                              status: String?,
                              errorMessage: String,
                              throwable: Throwable
                          ) {
                              LoggerUtil.info("Failed")
                          }
      
                      })
      
    2. Process Checkout with Bank Details:
      Once the bank details are obtained, incorporate them into the payload for checkout processing.

       portOne.checkoutUsingDirectBankTransfer(
                      PaymentDto.CheckoutWithDirectBankTransferRequest()
                  )
      

      The upcoming request mirrors the structure of the checkout request object without tokenization, with the exception of the final additional object appended to the request.

      PaymentDto.CheckoutWithDirectBankTransferRequest()

      ParametersData Type
      portoneKeyStringmandatory
      paymentChannelStringmandatory
      paymentMethodStringmandatory
      merchantOrderIdStringmandatory
      amountDoublemandatory
      currencyStringmandatory
      billingDetailsCheckoutPaymentDto.BillingDetailsOptional
      shippingDetailsCheckoutPaymentDto.ShippingDetailsOptional
      orderDetailsList<CheckoutPaymentDto.OrderDetail>Optional
      successUrlStringmandatory
      failureUrlStringmandatory
      environmentStringmandatory
      signatureHashStringmandatory
      redirectUrlStringmandatory
      descriptionStringOptional
      sourceStringmandatory
      transactionTypeStringmandatory
      DBTDetailsPaymentDto.DBTDetailsmandatory

      PaymentDto.DBTDetails

      ParametersData Type
      customerNameStringmandatory
      transactionTimeStringmandatory
      amountPaidDoublemandatory

    Instalments

    To initiate an Instalments checkout, the following steps need to be taken:

    1. Retrieve Bank List for Instalments:
      Initially, fetching the bank list that offers instalment options is necessary to process instalment payments.

      portOne.getBankList(
                      channel = paymentChannel,
                      request = PaymentDto.BankListRequest(),
                      object : ApiCallbackInterface<PaymentDto.BankListResponse> {
                          override fun onSucceed(response: PaymentDto.BankListResponse) {
                              LoggerUtil.info("Successful")
                          }
      
                          override fun onFailure(
                              errorCode: Int?,
                              status: String?,
                              errorMessage: String,
                              throwable: Throwable
                          ) {
                              LoggerUtil.info("Failed")
                          }
      
                      })
      

      PaymentDto.BankListRequest()

      ParametersData Type
      amountDoublemandatory
      environmentStringmandatory
      portoneKeyStringmandatory
      isMerchantSponsoredBooleanmandatory
      paymentMethodStringmandatory
      overrideDefaultBooleanmandatory
      currencyStringmandatory

      The response will include a list of banks and associated terms which must be included in the request body for further processing.

    2. Request Parameters for Instalments:

      The subsequent request parameters are consistent with the generic checkout request object without tokenization, apart from the addition of a supplementary object for Instalments.

      portOne.checkoutUsingInstallation(
                      PaymentDto.CheckoutWithInstallationRequest()
                  )
      

      PaymentDto.CheckoutWithInstallationRequest()

      ParametersData Type
      portoneKeyStringmandatory
      paymentChannelStringmandatory
      paymentMethodStringmandatory
      merchantOrderIdStringmandatory
      amountDoublemandatory
      currencyStringmandatory
      billingDetailsCheckoutPaymentDto.BillingDetailsOptional
      shippingDetailsCheckoutPaymentDto.ShippingDetailsOptional
      orderDetailsList<CheckoutPaymentDto.OrderDetail>Optional
      successUrlStringmandatory
      failureUrlStringmandatory
      environmentStringmandatory
      signatureHashStringmandatory
      redirectUrlStringmandatory
      descriptionStringOptional
      sourceStringmandatory
      transactionTypeStringmandatory
      bankDetailsPaymentDto.BankDetailsmandatory

      PaymentDto.BankDetails

      ParametersData Type
      bankCodeStringmandatory
      bankNameStringmandatory
      isMerchantSponsoredBooleanmandatory
      overrideDefaultBooleanmandatory
      installmentPeriodPaymentDto.TermObjectmandatory

      PaymentDto.TermObject

      ParametersData Type
      monthIntmandatory

    For non-tokenized methods such as Direct Bank Transfer and Installments, the method for receiving the response is already included during integration, which is the payment status listener.

Merchant Centric Card Vault

The Merchant Centric Card Vault operates by enrolling merchants initially, followed by adding customers specific to each merchant. Cards are then saved based on individual customers, ensuring a personalized and secure card storage system. Several methods are available to facilitate various operations within this card vault setup.

  1. The subsequent method is utilised for customer addition.

    portOne.addCustomer(
                    token = token,
                    portOneKey = portoneKey,
                    subMerchantKey = subMerchantKey,
                    request = PaymentDto.AddCustomerRequest(
                        name = name,
                        phoneNo = phoneNo,
                        email = email,
                        customerRef = customerRef
                    ),
                    object : ApiCallbackInterface<PaymentDto.AddCustomerResponse> {
                        override fun onSucceed(response: PaymentDto.AddCustomerResponse) {
                            val gson = Gson()
                            val json = gson.toJson(response)
                            LoggerUtil.info("Successful")
                        }
    
                        override fun onFailure(
                            errorCode: Int?,
                            status: String?,
                            errorMessage: String,
                            throwable: Throwable
                        ) {
                            LoggerUtil.info("Failed")
                        }
    
                    })
    
    ParametersData Type
    tokenStringmandatory
    portoneKeyStringmandatory
    subMerchantKeyStringOptional
    nameStringmandatory
    phoneNoStringmandatory
    emailStringmandatory
    customerRefStringmandatory
  2. The following method is used to save a particular card for a specific customer.

    portOne.addCardForCustomer(
                    customerUUID = customerUUID,
                    token = token,
                    portoneKey = portoneKey,
                    subMerchantKey = subMerchantKey,
                    request = PaymentDto.NewCard(),
                    object : ApiCallbackInterface<PaymentDto.AddCardsResponse> {
                        override fun onSucceed(response: PaymentDto.AddCardsResponse) {
                            LoggerUtil.info("Successful")
                        }
    
                        override fun onFailure(
                            errorCode: Int?,
                            status: String?,
                            errorMessage: String,
                            throwable: Throwable
                        ) {
                            LoggerUtil.info("Failed")
                        }
    
                    })
    
    ParametersData Type
    customerUUIDStringmandatory
    tokenStringmandatory
    portoneKeyStringmandatory
    subMerchantKeyStringOptional

    PaymentDto.NewCard

    ParametersData Type
    cardNumberStringmandatory
    cardTypeStringmandatory
    cardholderNameStringmandatory
    serviceCodeStringmandatory
    expiryYearStringmandatory
    expiryMonthStringmandatory
    environmentStringmandatory
    portoneKeyStringmandatory
  3. This method is utilized to retrieve the stored cards based on the customer.

     portOne.listCardsForCustomer(
                    customerUUID = customerUUID,
                    token = token,
                    portoneKey = portoneKey,
                    subMerchantKey = subMerchantKey,
                    object : ApiCallbackInterface<PaymentDto.ListCardsForCustomerResponse> {
                        override fun onSucceed(response: PaymentDto.ListCardsForCustomerResponse) {
                            LoggerUtil.info("Successful")
                        }
    
                        override fun onFailure(
                            errorCode: Int?,
                            status: String?,
                            errorMessage: String,
                            throwable: Throwable
                        ) {
                            LoggerUtil.info("Failed")
                        }
    
                    })
    
    ParametersData Type
    customerUUIDStringmandatory
    tokenStringmandatory
    portoneKeyStringmandatory
    subMerchantKeyStringOptional
  4. This method is employed to remove a specific card for an individual customer.

     portOne.deleteCardForCustomer(
                    customerUUID = customerUUID,
                    token = token,
                    portoneKey = portoneKey,
                    subMerchantKey = subMerchantKey,
                    request = PaymentDto.DeleteCardRequest(token = cardToken),
                    object : ApiCallbackInterface<PaymentDto.GenericResponse> {
                        override fun onSucceed(response: PaymentDto.GenericResponse) {
                            LoggerUtil.info("Successful")
                        }
    
                        override fun onFailure(
                            errorCode: Int?,
                            status: String?,
                            errorMessage: String,
                            throwable: Throwable
                        ) {
                            LoggerUtil.info("Failed")
                        }
    
                    })
    
    
    ParametersData Type
    customerUUIDStringmandatory
    tokenStringmandatory
    portoneKeyStringmandatory
    subMerchantKeyStringOptional
    ParametersData TypeRemarks
    cardTokenStringmandatoryThe Card Token can be retrieved in the above fetching the list of cards method

Pre-authorization and Payment Capture

During Pre-authorization, the transaction is initially approved, and at a later time or within specified days, the payment can be processed using the Capture API.

To designate a transaction for pre-authorization, a specific parameter must be configured in the payload:

transactionType = *PREAUTH*


    val paymentDetails= PaymentDto.CheckoutUsingTokenizationRequest()
    paymentDetails.transactionType= "PREAUTH"

💡 Please note that Pre-authorization is applicable only for credit card payments with a smooth workflow.

Following the pre-authorization of a transaction, the subsequent method is utilized for capturing the transaction.

portOne.captureTransaction(
                orderReference = orderReference,
                portoneKey = portoneKey,
                token = token,
                object : ApiCallbackInterface<PaymentDto.GenericResponse> {
                    override fun onSucceed(response: PaymentDto.GenericResponse) {
                        LoggerUtil.info("Successful")
                    }

                    override fun onFailure(
                        errorCode: Int?,
                        status: String?,
                        errorMessage: String,
                        throwable: Throwable
                    ) {
                        LoggerUtil.info("Failed")
                    }

                })
ParametersData Type
orderReferenceStringmandatory
portoneKeyStringmandatory
tokenStringmandatory

Failover Routing

Failover routing is a functionality designed to ensure seamless credit card payment processing. This feature allows the configuration of primary and secondary payment channels through the admin portal. If a payment attempt using the primary channel fails for any reason, the system automatically redirects the user to the secondary payment channel.

Following method is used to fetch routes created in admin portal.

portOne.getRoutesList(
                token = token,
                portoneKey = portoneKey,
                object : ApiCallbackInterface<PaymentDto.RoutesListResponse> {
                    override fun onSucceed(response: PaymentDto.RoutesListResponse) {
                        val gson = Gson()
                        val json = gson.toJson(response)
                        LoggerUtil.info("Successful")
                    }

                    override fun onFailure(
                        errorCode: Int?,
                        status: String?,
                        errorMessage: String,
                        throwable: Throwable
                    ) {
                        LoggerUtil.info("Failed")
                    }

                })

This method provides the routeRef that should be included in the payload as outlined below:

  1. Enable routing by setting isRoutingEnabled to true.
  2. Specify the Routing Param type as failover.
  3. Include the Routing Ref, which is configured in the merchant portal.

    val paymentDetails = PaymentDto.CheckoutUsingTokenizationRequest()
    paymentDetails.isRoutingEnabled= true     // true || false
    paymentDetails.routingParams= PaymentDto.RoutingParams(type = "failover", routeRef)

Possible Error Scenarios:

INVALID_UNAUTHORIZED_JWT_TOKEN_ERROR

  1. Ensure that the PortOne Key and Secret Key belong to the same account.
  2. Confirm that the Secret Key has not been altered.
  3. Verify that the Bearer keyword precedes the generated token with a space. Example: Bearer $jwtToken.
  4. Check if the token expiration time is after the current time.

INVALID_UNAUTHORIZED_TRANSACTION_SIGNATURE_ERROR

  1. Validate if all parameters align with the payload/request.
  2. Ensure that the PortOne key matches with the payload and the account.

INVALID_UNAUTHORIZED_TRANSACTION_IAMPORTKEY_ERROR

  1. Confirm that the PortOne key matches with the payload and the account.

INVALID_PAYMENT_CHANNEL

  1. Validate that the payment channels and methods included in the payload are enabled in the PortOne portal.

INVALID_ENVIRONMENT

  1. Verify that an environment (sandbox or live) has been specified.

Summation of order value, tax, duties, shipping, and discount should equal the total amount

  1. If items are provided, ensure that the values match the total amount calculation formula: sum(items price * items quantity) + shipping charge - discount = amount.
  2. Mandatory parameters in the payload:
    • price
    • promo_discount (0 accepted)
    • shipping_charges (0 accepted)