RN Connect


Integrate with React Native SDK


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


Video Tutorial


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
  • Login to the portone portal where you can access the API Keys (client key and secret key) for integrations under Settings -> API tab.

Integration

Steps to integrate your React native application with PortOne React native SDK.

1. Enable deep link in iOS

  1. To open your application, add the url schemes to the app, Go to ProjectSettings -> info

  2. Add url schemes and identifier inside the URL types.
    You can see the scheme and identifier details in info.plist as below:

    ```json
    <key>CFBundleURLTypes</key>
    <array>
    		<dict>
    			<key>CFBundleTypeRole</key>
    			<string>Editor</string>
    			<key>CFBundleURLName</key>
    			<string>checkout</string>
    			<key>CFBundleURLSchemes</key>
    			<array>
    				<string>portone</string>
    			</array>
    		</dict>
    	</array>
    ```
    
  3. To open the other applications, should include the url schemes in info.plist

    <key>LSApplicationQueriesSchemes</key>
    	<array>
    		<string>itms-appss</string>
    		<string>zalopay</string>
    		<string>line</string>
    		<string>ascendmoney</string>
    	</array>
    

    LSApplicationQueriesSchemes - Specifies the URL schemes you want the app to be able to use with the canOpenURL: method of the UIApplication class.

  4. To support HTTP connections, add this source code in app info.plist

    <key>NSAppTransportSecurity</key>
    	<dict>
    		<key>NSAllowsArbitraryLoads</key>
    		<true/>
    		<key>NSAllowsLocalNetworking</key>
    		<true/>
    		<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
    		<true/>
    	</dict>
    

2. Enable deep link in ANDROID

  1. To create a link to your app content, add an intent filter that contains these elements and attribute values in your AndroidManifest.xml:

  2. Include the BROWSABLE category. It is required in order for the intent filter to be accessible from a web browser. Without it, clicking a link in a browser cannot resolve to your app.

    Also include the DEFAULT category. This allows your app to respond to implicit intents. Without this, the activity can be started only if the intent specifies your app component name.

    ```java
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    

To accept the URIs that begin with portone://checkout“

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>


3. Add following steps to use the librbary

  1. Add .npmrc file to the project directory

  2. In that file, add the following code and save it

    @iamport-intl:registry=https://npm.pkg.github.com/
    //npm.pkg.github.com/:_authToken=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
    
  3. Go to project terminal, run the following code

    npm install @iamport-intl/portone-sdk@latest
    

    After successful installation, it will be shown in package.json under dependencies

  4. Add peer dependencies

    npm install @react-native-async-storage/async-storage
    npm install react-native-localization
    npm install react-native-webview
    npm install react-native-linear-gradient
    npm install react-native-rsa-native
    

4. 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


5. Generate Signture Hash

Generate a Signature Hash using HmacSHA256 which needs to be included in the payload.

createHash = (
    key,
    amount,
    currency,
    failureUrl,
    merchantOrderId,
    successUrl,
    secretKey,
  ) => {
    let mainParams =
      'amount=' +
      encodeURIComponent(amount) +
      '&client_key=' +
      encodeURIComponent(key) +
      '&currency=' +
      encodeURIComponent(currency) +
      '&failure_url=' +
      encodeURIComponent(failureUrl) +
      '&merchant_order_id=' +
      encodeURIComponent(merchantOrderId) +
      '&success_url=' +
      encodeURIComponent(successUrl);
    console.log('currency', currency);
    let hash = HmacSHA256(mainParams, secretKey);
    let signatureHash = Base64.stringify(hash);
    return signatureHash;
  };

6. Initialise PortOne RN SDK and authorise it.

  1. Import library of portone SDK

import {Checkout} from '@iamport-intl/chaiport-sdk';
  1. Initialize the checkout instance as below:
<Checkout
          callbackFunction={this.afterCheckout}
          redirectUrl={"chaiport://"}
          environment={"sanbox"} //Optional
/>

Different Payment Methods

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.

New Credit card for a particular number

  1. Collect the card details and provide them to startPaymentWithNewCard method as below:

    // @param {object} cardDetails
    // @param {object} payload
    // @param {string} JWTToken
    // @param {string} clientKey
    // @param {string} customerUUID - provide it when merchant card vault enabled
    // @param {string} subMerchantKey - provide it when subMerchant flow enabled
        let response = await Checkout.startPaymentWithNewCard(
            cardDetails,
            payload,
            JWTToken,
            clientKey,
            customerUUID,
            subMerchantKey
        );
    

    All the data types in the form of an object

    ParametersData Type
    cardDetailsObjectmandatory
    JWTTokenstringmandatory
  2. Web view response will be given back to the callbackFunction

    afterCheckout = transactionDetails => {
        console.log('Response from webview', transactionDetails);
        // Do the needful
      };
    

Sample Payload:


//Card details
    {
  card_number: data.cardNumber,
  card_holder_name: data.name || 'NGUYEN VAN A',
  cvv: data.cvv,
  expiry_month: data.expirationMonth,
  expiry_year: data.expirationYear,
  saved_card: data?.saveForLater,
}

let orderDetails = [
    {
        id: `${item.key}`,
        price: item.price,
        name: item.name,
        quantity: item.quantity,
        image: item.img,
    }
]

let totalAmount = sum of orderDetails +  shipping - promoDiscount
let payload =    {
        portOneKey: PORTONEKEY,
        amount: totalAmount,
        merchantOrderId: merchantOrderId,
        paymentChannel: "MASTERCARD",
        paymentMethod: `MASTERCARD_CARD`,
        currency: CURRENCY,
        environment: ENVIRONMENT,
        merchantDetails: {
            name: 'Chaipay Cart',
            logo: 'https://demo.portone.cloud/images/chikku-loafers.jpg',
            back_url: 'https://demo.chaipay.io/checkout.html',
            promo_code: 'Downy350',
            promo_discount: promoDiscount,
            shipping_charges: shipping,
        },
        
        signatureHash: "123" 
        
        billingDetails: {
            billing_name: 'Test React native',
            billing_email: '[email protected]',
            billing_phone: '+660956425564',
            billing_address: {
            city: 'VND',
            country_code: 'VN',
            locale: 'en',
            line_1: 'address',
            line_2: 'address_2',
            postal_code: '400202',
            state: 'Mah',
            },
        },
        shippingDetails: {
            shipping_name: 'xyz-Testing',
            shipping_email: '[email protected]',
            shipping_phone: '+910830443596',
            shipping_address: {
            city: 'testing city',
            country_code: 'VN',
            locale: 'en',
            line_1: 'Testing line 1',
            line_2: 'Testing line 2',
            postal_code: '400202',
            state: 'Mah',
            },
        },
        
        orderDetails: orderDetails,
        successUrl: successURL,
        failureUrl: failureURL,
        redirectUrl: 'demo://checkout1',
        
        source: 'mobile'
    }

sample JWT token

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJDSEFJUEFZIiwic3ViIjoibHpyWUZQZnlNTFJPYWxsWiIsImlhdCI6MTYzMjM5MDkyMCwiZXhwIjoyNzMyMzkwOTIwfQ.IRgiM-zjAdJEVDuPSNfxmDszZQi_csE1q7xjVRvPvoc';

Sample response :

  • Success Case
    {
        "data": {
            "is_success": "true",
            "redirect_url": "",
            "channel_order_ref": "1xAEm5eZ4sbhzqaPEIV4tVoAXMm",
            "merchant_order_ref": "MERCHANT1629789647638",
            "order_ref": "1xAEm5eZ4sbhzqaPEIV4tVoAXMm",
            "message": "",
            "deep_link": "",
            "additional_data": null
        },
        "status": 200,
    }
  • Failure case
    {
        "data": {
            "is_success": "false",
            "redirect_url": "",
            "channel_order_ref": "1xAFIwrpWxarXuHGu3PMe0syPg0",
            "merchant_order_ref": "MERCHANT1629789909578",
            "order_ref": "1xAFIwrpWxarXuHGu3PMe0syPg0",
            "message": "",
            "deep_link": "",
            "additional_data": null
        },
        "status": 400,
    }

Saved Card for a particular number

  1. Collect the card details and provide them to the startPaymentWithSavedCard method as below:

    // @param {String} cardDetails
    // @param {object} payload
    // @param {string} JWTToken
    // @param {string} clientKey
        let response = await Checkout.startPaymentWithSavedCard(
            cardDetails,
            payload,
            JWTToken,
            clientKey
        );
    

Params:

ParametersData TypeMandatory
Card DetailsObjectyes
payloadObjectYes
JWTTokenStringYes
clientKeyStringYes

Card Details:

ParametersData TypeMandatory
partial_card_numberObjectyes
expiry_yearObjectYes
expiry_monthStringYes
cardTypeStringYes
tokenStringYes

Payload

    let orderDetails = [
    {
        id: `${item.key}`,
        price: item.price,
        name: item.name,
        quantity: item.quantity,
        image: item.img,
    }
    ]

    let totalAmount = sum of orderDetails +  shipping - promoDiscount
    let payload =    {
        portOneKey: PORTONEKEY,
        amount: totalAmount,
        merchantOrderId: merchantOrderId,
        paymentChannel: "MASTERCARD",
        paymentMethod: `MASTERCARD_CARD`,
        currency: CURRENCY,
        environment: ENVIRONMENT,
        countryCode: 'VN',
        merchantDetails: {
            name: 'Chaipay Cart',
            logo: 'https://demo.portone.cloud/images/chikku-loafers.jpg',
            back_url: 'https://demo.chaipay.io/checkout.html',
            promo_code: 'Downy350',
            promo_discount: promoDiscount,
            shipping_charges: shipping,
        },
        
        signatureHash: "123" 
        
        billingDetails: {
            billing_name: 'Test React native',
            billing_email: '[email protected]',
            billing_phone: '+660956425564',
            billing_address: {
            city: 'VND',
            country_code: 'VN',
            locale: 'en',
            line_1: 'address',
            line_2: 'address_2',
            postal_code: '400202',
            state: 'Mah',
            },
        },
        shippingDetails: {
            shipping_name: 'xyz-Testing',
            shipping_email: '[email protected]',
            shipping_phone: '+910830443596',
            shipping_address: {
            city: 'testing city',
            country_code: 'VN',
            locale: 'en',
            line_1: 'Testing line 1',
            line_2: 'Testing line 2',
            postal_code: '400202',
            state: 'Mah',
            },
        },
        
        orderDetails: orderDetails,
        successUrl: successURL,
        failureUrl: failureURL,
        redirectUrl: 'demo://checkout1',
        
        source: 'mobile',
        token_params: {
            partial_card_number: "";
            expiry_year: ""; // "yyyy"
            expiry_month: ""; // "dd"
            cardType: "";
            token: ""
        }
    }

Success Response

{
    "data": {
        "is_success": "true",
        "redirect_url": "",
        "channel_order_ref": "1xAEm5eZ4sbhzqaPEIV4tVoAXMm",
        "merchant_order_ref": "MERCHANT1629789647638",
        "order_ref": "1xAEm5eZ4sbhzqaPEIV4tVoAXMm",
        "message": "",
        "deep_link": "",
        "additional_data": null
    },
    "status": 200,
}

Failed Response

{
    "data": {
        "is_success": "false",
        "redirect_url": "",
        "channel_order_ref": "1xAFIwrpWxarXuHGu3PMe0syPg0",
        "merchant_order_ref": "MERCHANT1629789909578",
        "order_ref": "1xAFIwrpWxarXuHGu3PMe0syPg0",
        "message": "",
        "deep_link": "",
        "additional_data": null
    },
    "status": 400,
}

Wallet

Collect the payload and provide them to the startPaymentWithWallets method as below:

// @param {object} payload
// @param {string} subMerchantKey - provide it when sub merchant flow is enabled
let data =  await Checkout.startPaymentWithWallets(payload, subMerchantKey);

Initate Payment with non tokenize flow

Collect the payload and provide them to the startPaymentWithoutTokenization method as below:

// @param {object} newPayload
// @param {string} subMerchantKey - provide it when sub merchant flow is enabled

let data =  await Checkout.startPaymentWithoutTokenization(newPayload, subMerchantKey);

**Fetch Available payment methods**

Collect the portOneKey, currency and provide them to the getAvailablePaymentMethods method as below:

// @param {String} portOneKey
// @param {string} currency
// @param {string} subMerchantKey - provide it when sub merchant flow is enabled

let data =  await Checkout.getAvailablePaymentMethods(portOneKey, currency, subMerchantKey);

Merchant centric card vault

  1. Collect the customerId, clientKey, JWTToken and cardData and provide them to the addCardForCustomerId method as below:

    import {helpers} from '@iamport-intl/portone-sdk';
    // @param {String} customerId
    // @param {string} clientKey
    // @param {string} JWTToken
    // @param {object} cardData
    // @param {string} subMerchantKey - provide it when sub merchant flow is enabled
    let data =  await helpers.addCardForCustomerId(customerId, clientKey, JWTToken, cardData, subMerchantKey);
    

    • cardData

    {
        
        "card_number": "42424242424244242",
        "expiry_month": "07",
        "expiry_year": "31",
        "type": "visa"
    }
    
  2. Collect the customerId, clientKey, JWTToken and cardData and provide them to the deleteCardForCustomerId method as below:

    // @param {String} customerId
    // @param {string} clientKey
    // @param {string} JWTToken
    // @param {object} cardData
    // @param {string} subMerchantKey - provide it when sub merchant flow is enabled
    let data =  await helpers.deleteCardForCustomerId(customerId, clientKey, JWTToken, cardData, subMerchantKey);
    
    • CardData
    {
        "token": "cdec91449d3a4b4bae9144d586a2b972",
    }
    
  3. Collect the customerId, clientKey, JWTToken and provide them to the fetchCustomerCards method as below

    // @param {String} customerId
    // @param {string} clientKey
    // @param {string} JWTToken
    // @param {object} cardData
    // @param {string} subMerchantKey - provide it when sub merchant flow is enabled
    let data =  await helpers.fetchCustomerCards(customerId, clientKey, JWTToken, subMerchantKey);
    
  4. Collect the customerData, clientKey, JWTToken and provide them to the addCustomer method as below:

    // @param {object} customerData
    // @param {string} clientKey
    // @param {string} JWTToken
    // @param {string} subMerchantKey - provide it when sub merchant flow is enabled
    let data =  await helpers.addCustomer(customerData, clientKey, JWTToken, subMerchantKey);
    
    • customerData
      {
      "name": " User",
      "phone_number": "+9183414691",
      "email_address": "[email protected]",
      "customer_ref": "867348767863w5877"
      }
      
  5. Collect the customerID, customerData, clientKey, JWTToken and provide them to the getCustomerData method as below:

    let data =  await helpers.getCustomerData(customerID, clientKey, JWTToken);
    

Failover Routing

To support failover routing add the following two parameters to payload:

  • isRoutingEnabled= true
  • Routing Param type should be failover.
  • provide the Routing Ref which is set in the merchant portal.
        let payload = getDefaultPayload()
        payload.isRoutingEnabled = true
        payload.routingParams={
            type: "failover", 
            routeRef: "1233"
        }
    
  1. To Fetch the List of Routes which are created from merchant portal

    1. Collect the clientKey, JWTToken and provide them to the fetchRoutes method as below:

      let data =  await helpers.fetchRoutes(clientKey, JWTToken);
      

PreAuth and Capture Payment

  1. To implement PreAuth one parameter has to be set: transactionType= PREAUTH || PURCHASE

        let payload = getDefaultPayload()
        payload.transactionType = "PREAUTH"
    
  2. To Capture the Payment

    • Collect the transactionOrderRef, clientKey, JWTToken and provide them to the captureTransactionAPI method as below:

    let data =  await helpers.captureTransactionAPI(transactionOrderRef, clientKey, JWTToken);
    

Payload

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

ParametersData Type
portOneKeyStringmandatory
merchantDetailsobject
merchantOrderIdStringmandatory
signatureHashStringmandatory
amountDoublemandatory
currencyStringmandatory
countryCodeStringmandatory
billingDetailsobjectOptional
shippingDetailsobjectOptional
orderDetailsarrayOptional
successUrlStringmandatory
failureUrlStringmandatory
expiryHoursIntmandatory
sourceStringmandatory
descriptionStringOptional
showShippingDetailsBooleanOptional
showBackButtonBooleanOptional
defaultGuestCheckoutBooleanOptional
isCheckoutEmbedBooleanOptional
redirectUrlStringmandatory
environmentStringmandatory

MerchantDetails

ParametersData Type
nameStringOptional
logoStringOptional
back_urlStringOptional
promo_codeStringOptional
promoD_discountIntOptional
shipping_chargesDoubleOptional

BillingDetails

ParametersData Type
shipping_nameStringOptional
shipping_emailStringOptional
shipping_phoneStringOptional
shipping_addressObjectOptional

**ShippingDetails**

ParametersData Type
billing_nameStringOptional
billing_emailStringOptional
billing_phoneStringOptional
billing_addressObjectOptional

Address

ParametersData Type
cityStringOptional
country_codeStringOptional
localeStringOptional
line_1StringOptional
line_2StringOptional
postal_codeStringOptional
stateStringOptional

OrderDetail

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

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)