Android Embed

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

Video Title

Part-2

Video Title

Sample App

Check the sample app

Prerequisites

  1. Create an account on PortOne
  2. Enable Payment Channels and Methods of your choice
  3. authKey to access

📘

authKey

authKey will be issued by the PortOne Team.

  1. Send an email to [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 to receive the payment status

  1. If we have an app-to-app flow, our app will receive a deep link via intent after checkout, which we will provide to the SDK using the method below.
if (null != intent && null != intent.data) {
        val paymentStatus = intent.data.toString() 
        portone.updatePaymentStatus(paymentStatus)
}
  1. The following method has to be added on the checkout activity where the checkout action has performed to receive the checkout status after checkout completion the HashMap format.
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
                }

            }

        }
    }


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 that will be included in the payload.

internal fun getSignatureHash(
        amount: String,
        currency: String,
        failureUrl: String,
        orderId: String,
        clientKey: String,
        successUrl: String
    ): String {
        val hashMap: HashMap<String, String> = HashMap()
        hashMap["amount"] = amount
        hashMap["currency"] = currency
        hashMap["failure_url"] = failureUrl
        hashMap["merchant_order_id"] = orderId
        hashMap["client_key"] = clientKey
        hashMap["success_url"] = successUrl

        val message = StringBuilder()
        for ((key, value) in hashMap.toSortedMap().entries) {
            val values = URLEncoder.encode(value, "UTF-8")
            if (message.isNotEmpty()) {
                message.append("&$key=$values")
            } else {
                message.append("$key=$values")
            }
        }

        val sha256 = Hashing.hmacSha256(SECRET_KEY.toByteArray(StandardCharsets.UTF_8))
            .hashString(message, StandardCharsets.UTF_8).asBytes()

        val base64: String = Base64.encodeToString(sha256, Base64.DEFAULT)
        Log.i(TAG_CHAI_PAY, "SignatureHash:base64-> $base64")

        return base64.trim()
    }


Checkout using Web (V3)

Our web checkout method involves processing of web-based payouts. To do this, we need to call a method, but first we need to prepare the method’s parameters, which include the following.

Web Checkout Process

All the data types in the form of an object

ParametersData Type
tokenStringmandatory
clientKeyStringmandatory
requestCheckoutPaymentDto.CheckoutUsingWebRequestmandatory

CheckoutPaymentDto.CheckoutUsingWebRequest

All of the web checkout request's parameters are listed here, along with the appropriate data type.

ParametersData Type
chaipayKeyStringmandatory
merchantDetailsCheckoutPaymentDto.MerchantDetails
merchantOrderIdStringmandatory
signatureHashStringmandatory
amountDoublemandatory
currencyStringmandatory
countryCodeStringmandatory
billingDetailsCheckoutPaymentDto.BillingDetailsOptional
shippingDetailsCheckoutPaymentDto.ShippingDetailsOptional
orderDetailsList<CheckoutPaymentDto.OrderDetail>Optional
successUrlStringmandatory
failureUrlStringmandatory
expiryHoursIntmandatory
sourceStringmandatory
descriptionStringOptional
showShippingDetailsBooleanOptional
showBackButtonBooleanOptional
defaultGuestCheckoutBooleanOptional
isCheckoutEmbedBooleanOptional
redirectUrlStringmandatory
environmentStringmandatory

CheckoutPaymentDto.MerchantDetails

ParametersData Type
nameStringOptional
logoStringOptional
backUrlStringOptional
promoCodeStringOptional
promoDiscountIntOptional
shippingChargesDoubleOptional

CheckoutPaymentDto.BillingDetails

ParametersData Type
shippingNameStringOptional
shippingEmailStringOptional
shippingPhoneStringOptional
shippingAddressCheckoutPaymentDto.AddressOptional

CheckoutPaymentDto.ShippingDetails

ParametersData Type
billingNameStringOptional
billingEmailStringOptional
billingPhoneStringOptional
billingAddressCheckoutPaymentDto.AddressOptional

CheckoutPaymentDto.Address

ParametersData Type
cityStringOptional
countryCodeStringOptional
localeStringOptional
line_1StringOptional
line_2StringOptional
postal_codeStringOptional
stateStringOptional

CheckoutPaymentDto.OrderDetail

ParametersData Type
idStringOptional
priceDoubleOptional
nameStringOptional
quantityIntOptional
imageString (in the form of web url)Optional

Sample dumy Request:

    fun getWebCheckoutRequest(): CheckoutPaymentDto.CheckoutUsingWebRequest {

        val orderId = "merchant_order_id_001"
        val signatureHash =
            Utility.getSignatureHash(
                "50010",
                currency,
                "https://dev-checkout.portone.io/failure.html",
                orderId,
                clientKey,
                "https://dev-checkout.portone.io/success.html"
            )

        val orderDetail: CheckoutPaymentDto.OrderDetail = CheckoutPaymentDto.OrderDetail(
            id = "knb",
            name = "kim nguyen bao",
            price = 50010.00,
            quantity = 1,
            image = "https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.amazon.in%2FWORLD-WEAR-FOOTWEAR-Red-1243-Comfortable%2Fdp%2FB07WRZW939&psig=AOvVaw3EEZHcjsC6i5FltZjpjYjB&ust=1709615063971000&source=images&cd=vfe&opi=89978449&ved=0CBMQjRxqFwoTCNCV_cfq2YQDFQAAAAAdAAAAABAD"
        )

        val orderList = ArrayList<CheckoutPaymentDto.OrderDetail>()
        orderList.add(orderDetail)

        val orderDetails = CheckoutPaymentDto.CheckoutUsingWebRequest()
        orderDetails.chaipayKey = clientKey
        orderDetails.merchantDetails = CheckoutPaymentDto.MerchantDetails(
            name = "Gumnam",
            logo = "https://www.google.com/imgres?imgurl=https%3A%2F%2Fcdn.dribbble.com%2Fuserupload%2F10790076%2Ffile%2Foriginal-ff791b20101e6551b2ade568108f5dba.png%3Fresize%3D400x300%26vertical%3Dcenter&tbnid=jYyQOvc0cqGZIM&vet=12ahUKEwjO08r-6tmEAxU9_DgGHdetAnEQMygMegUIARCIAQ..i&imgrefurl=https%3A%2F%2Fdribbble.com%2Fsearch%2Flogo&docid=95YyYbaoa7wZ4M&w=400&h=300&q=logo&ved=2ahUKEwjO08r-6tmEAxU9_DgGHdetAnEQMygMegUIARCIAQ",
            backUrl = "https://demo.portone.io/checkout.html",
            promoCode = "Gumnam420",
            promoDiscount = 10000,
            shippingCharges = 10000.00
        )

        orderDetails.merchantOrderId = orderId
        orderDetails.signatureHash = signatureHash.trim()
        orderDetails.amount = 50010.00
        orderDetails.currency = currency
        orderDetails.countryCode = "VN"
        orderDetails.billingDetails = CheckoutPaymentDto.BillingDetails(
            billingName = "Test mark",
            billingEmail = "[email protected]",
            billingPhone = phoneNo,
            billingAddress = CheckoutPaymentDto.Address(
                city = currency, countryCode = "VN", locale = "en",
                line_1 = "address",
                line_2 = "address_2",
                postal_code = "400202",
                state = "Mah"
            )
        )
        orderDetails.shippingDetails = CheckoutPaymentDto.ShippingDetails(
            shippingName = "Test mark",
            shippingEmail = "[email protected]",
            shippingPhone = phoneNo,
            shippingAddress = CheckoutPaymentDto.Address(
                city = currency, countryCode = "VN", locale = "en",
                line_1 = "address",
                line_2 = "address_2",
                postal_code = "400202",
                state = "Mah"
            )
        )
        orderDetails.orderDetails = orderList
        orderDetails.successUrl = "https://dev-checkout.portone.io/success.html"
        orderDetails.failureUrl = "https://dev-checkout.portone.io/failure.html"
        orderDetails.expiryHours = 1
        orderDetails.source = "mobile"
        orderDetails.description = "By Aagam"
        orderDetails.showShippingDetails = true
        orderDetails.showBackButton = true
        orderDetails.defaultGuestCheckout = true
        orderDetails.isCheckoutEmbed = false
        orderDetails.env = devEnvironment
        orderDetails.redirectUrl = "portone://checkout"
        orderDetails.environment = environment

        return orderDetails

    }

This is the method that has been utilised to process the web checkout.

val checkoutDetails = CheckoutPaymentDto.CheckoutUsingWebRequest()
portone.checkoutUsingWeb(
            token = getJwtToken(clientKey),
            clientKey = clientKey,
            request = checkoutDetails
        )

After the payment completion the payment status will be received on onActivityResult we we have already added while integration.


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)