Android Connect


Integrate with Android SDK

Integrate PortOne Payment Gateway with your Android app using the PortOne Android SDK and start accepting payments.


You can use PortOne SDK to integrate the PortOne Payment Gateway with your Android Application.


Video Tutorial

Part-1 : Installation of Android Native SDK

Part 2: Initialise the SDK and Set Intent Filters to receive the Payment Status


Sample App

Check the sample app to integrate on GitHub


Prerequisites

  • Create an account on PortOne
  • Enable Payment Channels and Methods which you want to use
  • An Android application for the integration
  • authKey to access the SDK 💡 *authKey will be issued by the PortOne Team. 1.* Send an email to [[email protected]](mailto:[email protected]) to request the authKey.

Integration

Steps to integrate your Android application with PortOne Android SDK.

  1. Install PortOne Android SDK and authorise it.
  2. Initialise the PortOne Instance in the activity where the checkout is being processed.
  3. Set the Intent Filters in the manifests of the Android App.
  4. Set Intent Receivers to receive the payment status
  5. Have a setup to get JWT token from the server.
  6. Generate a signature hash to be passed with the payload

1. Install PortOne Android SDK and authorise it.

  1. To add PortOne SDK as a dependency, locate build.gradle (:app) in your Android app and add the following line

implementation 'com.github.iamport-intl:chaipay-android-native-sdk:V3.0.37'
  1. To access the PortOne SDK, you will require authorisation key to be added in your gradle.properties
authKey= XXXXXXXXXXXXXXXXXXXXXX
💡 *authKey will be issued by the PortOne Team. 1.* Send an email to [[email protected]](mailto:[email protected]) to request the authKey.
  1. We must utilize the authkey in order to access the PortOne SDK. To build.gradle (:Project) or settings.gradle, add the following snippets.
repositories {
        maven { url '<https://maven.google.com/>' }
        maven{
            url '<https://jitpack.io>'
            credentials { username authKey }
        }
    }
💡 *Starting with Gradle 7, Android suggests the use of [centralized repository declarations](https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:centralized-repository-declaration) in `settings.gradle` over project- or module-level `build.gradle` declarations.*
  1. Include the following lines of code in build.gradle (:Project), mentioning the project dependency and the kotlin version.
buildscript {
    ext.kotlin_version = "1.5.10"
    dependencies {
        classpath 'com.google.gms:google-services:3.0.0'
    }
}

2. Initialise the PortOne Instance in the activity where the checkout is being processed.

The PortOne object will be used to access checkout functions. You will additionally require to pass the environment, whether SANDBOX or LIVE.

private var environment = "sandbox"            // For Sandbox
private var environment = "live"               // For Live

private lateinit var portOne: PortOne
portone = PortOneImpl(this, "$environment")

3. Set the Intent Filters in the manifests of the Android App.

Add the following Intent Filter to the Activity to which you want the user to navigate after payment is completed. Default should be Checkout Activity.

To configure the intent filters, there are two parameters in the data tag: host and scheme, whose values will be determined by the redirection URL.

if redirectionUrl= "**portone://checkout"**
then host = "**checkout"**

     `scheme = "**portone"**`
<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:host="checkout"
                    android:scheme="portone" />
</intent-filter>


4. Set Intent Receivers and listeners to receive the payment status

  1. The following method is to set the listener to receive the payment status

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

On the server side, a JWT token must be constructed that accepts portoneKey as input. Further information on JWT token generation is described in the link below.

Authentication | PortOne


6. Generate Signture Hash

Make a Signature Hash on server side using secret key which will be included in the payload.

Payment Request | PortOne


Merchant’s Checkout (V2)

Merchant’s Checkout The Android Native SDK is designed so that merchants can use their own user interfaces based on the theme that best suits the app, and they can inherit business logic in the form of various methods based on functionalities that do not involve a user interface.

The following methods are described in depth along with their application:

  1. The following method is used to fetch the enabled payment channel and methods which are accessible for checkout.

    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. The following method is used to process the checkout using the the new card so it means if you have not saved this card before do the payment from the beginning using a card then this is the method to be used.

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

    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

    jwtToken

    💡 jwtToken is used for the Authentication in the Api which is being generated as per the instructions in the integration documentation.
  3. In order to do payment with the saved cards for the first time, the following method helps to fetch the cards associated with the phone number but to get cards OTP authentication is required so its divided into two steps:

    1. Fetching the saved cards for the first time

      1. The below method is used to send 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. After completion of the above step an OTP will be received on the phone. which will be used as the input.

        ParametersData Type
        tokenStringOptional
        portoneKeyStringmandatory
        phoneNoStringmandatory
        otpStringmandatory
        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. Fetching the saved cards multiple time
      After fetching the saved cards for the first time in the response, the token is being received which can also be as an alternative of the authentication so instead of the otp, the token can be passed in the token param.

      | Parameters  | Data Type |  |
      | --- | --- | --- |
      | token | String | mandatory |
      | portoneKey | String | mandatory |
      | phoneNo | String | mandatory |
      | otp | String | Optional |
      
      ```kotlin
      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. In order to process non-tokenise flow the following method is used. When we speak of non-tokenise checkout we include the payment methods which does not need to be tokenise.

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

    For the non-tokenise flow we need one object to be passed which contains the request for the checkout.

    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 above Params are common for all the non-tokenise checkout but according to the methods few additional params need to be passed with are described below.
    1. Direct Bank Transfer
    2. Instalments

    The above two methods are also falls into the category of non-tokenise flow but these methods require few additional params in the request so below the whole flow for these methods has been described.

    Direct Bank Transfer

    For Direct Bank Transfer checkout the following steps are required to be followed:

    1. Fetch the Direct Bank Transfer details using the following method which only takes portoneKey as input:

      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. After getting the bank details we can pass the details to payload and process checkout.

       portOne.checkoutUsingDirectBankTransfer(
                      PaymentDto.CheckoutWithDirectBankTransferRequest()
                  )
      

      The following Request is replica of he request object checkout without tokenisation except the last added object

    Instalments

    For Instalments checkout the following steps are required to be followed:

    1. To process the instalment fetching the bank list which provides instalments is required.

      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

      In the response a list of banks and terms will be provided which are needed to be pass in the request body.

      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
    2. The below request params are replica of of the request object checkout without tokenisation except the last added object

      portOne.checkoutUsingInstallation(
                      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

Merchant Centric Card Vault

In the merchant centric card vault first a merchant is being enrolled and then for that merchant the customers are being added and we save cards according to the unique customer and below few methods are provided to do operations.

  1. The following method is used to add customer.

    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. The following method is used to fetch the saved cards according to 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. The following method is used to delete any particular card for a specific 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 Auth and Capture Payment

In Pre Authorisation the transaction is authorised first and after a certain time or days that payment can be captured using the Capture Api.

To set any transaction to be pre authorised one param need to be set in the payload which is

transactionType = *PREAUTH*


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

💡 Pre Auth can only be used in credit card payments where there is a seamless flow

After pre authorising the transaction, the following method is used to capture 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

Probable Errors:

INVALID_UNAUTHORIZED_JWT_TOKEN_ERROR

  1. Check whether PortOne Key and the Secret Key are of the same account
  2. Check whether the Secret Key is not modified
  3. Check whether Bearer keyword is added before the generated token with a white space. Bearer $jwtToken
  4. Verify if the expiration time should be greater than the current time

INVALID_UNAUTHORISED_TRANSACTION_SIGNATURE_ERROR

  1. Check whether all params match with the payload/request
  2. Check whether the portone key match with the payload and the account

INVALID_UNAUTHORISED_TRANSACTION_IAMPORTKEY_ERROR

  1. Check whether the portone key match with the payload and the account

INVALID_PAYMENT_CHANNEL

  1. Make sure the payment channels and payment methods which are added in the payload are enable from the portone portal

INVALID_ENVIRONMENT

  1. Make sure you have added environment either sandbox or live

Summation of order value, tax, duties, shipping and discount is equal to amount

  1. If items are provided then please verify the values provided should match the total amount: sum(items price * items quantity) + shipping charge - discount = amount
  2. Mandatory params in payload:
price
promo_discount       (0 is also acceptable)
shipping_charges     (0 is also acceptable)