Skip to main content

iOS Native SDK(v2) - Merchant's Checkout

Please follow steps below to setup iOS Native SDK and start accepting payment via PortOne.


Steps to integrate iOS SDK:#

  • Download the framework from here

  • After downloading the .xcframework, drag and drop it in the project

    • Go to General → Frameworks, Library and Embedded Content and then drop the framework and change the Embed to Embed & sign.

      9.png

  • Import the PortOnePaymentSDK as below at the required places.

    import ChaiPayPaymentSDK
  • Initialise the checkout instance to get the available methods in SDK as below

    var checkout = Checkout(environmentType: .dev, redirectURL: "chaipay://", delegate: self)
  • Set the environment as .dev to test in the dev environment. While moving to the Production, set the environment to .prod(default to .prod).

  • Should implement the delegate methods as below to get the response of failure and success callbacks from the webView.

    extension ViewController: CheckoutDelegate {
    func transactionResponse(_ webViewResponse: WebViewResponse?) {
    if let response = webViewResponse {
    // Do the needful with response
    }
    }
    var viewController: UIViewController? {
    return self
    }
    }
  • In the info.plist add the new Array type node LSApplicationQueriesSchemes as below:

    <key>LSApplicationQueriesSchemes</key>
    <array>
    <string>itms-appss</string>
    <string>momo</string>
    <string>zalopay</string>
    <string>chaipay</string>
    </array>

    10.png

  • Include the URLType in info.plist to redirect to your app(deep linking) as below

    <key>CFBundleURLTypes</key>
    <array>
    <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLName</key>
    <string>checkout</string>
    <key>CFBundleURLSchemes</key>
    <array>
    <string>chaipay</string>
    </array>
    </dict>
    </array>

    11.png

Fetch the saved cards for a particular number#

  • Capture the mobile number and OTP to fetch the saved credit cards for a particular user.
  • To generate the OTP, call the method as below:
checkOut?.getOTP(self.numberTextField.text ?? "") {result in
switch result {
case .success(let data):
print("data" , data)
case .failure(let error):
print("error", error)
break
}
}

data - Contains data, status.

Status code :

200 - Success

400 - Failed due to incorrect OTP or wrong params passed

  1. After successfully entered the OTP, the captured mobile number and OTP should pass to the fetchSavedCards as below to fetch the saved credit cards for the particular number.
checkOut?.fetchSavedCards(self.formattedText ?? "", otp: self.OTP ?? "") {result in
switch result {
case .success(let data):
//Do the needful
case .failure(let error):
// handle the error cases
print("errror", error)
}
}

formattedText - Contains mobile number with Country code. (eg: ⁨‭+16625655248 , +918341234123)

OTP - OTP that received to the particular number given.

response:

Success case

"data": [
{
"token": "5ff6644dec554fdc8f6d2e161a0c662d",
"partial_card_number": "450875******1019",
"expiry_month": "05",
"expiry_year": "2021",
"type": "visa"
},
{
"token": "5ff6644dec554fdc8f6d2e161a0c662d",
"partial_card_number": "450875******1019",
"expiry_month": "05",
"expiry_year": "2021",
"type": "visa"
}
],
"status": 200,
}

Failure case:

{
"message": "OTP is a required Query Param!",
"status": 400,
}

Initiate payments in following ways#

  • Initiate with wallet payment
  • Initiate with new credit card Payment
  • Initiate from saved credit card payment

Initiate the wallet Payment

  • Initialise the wallet payment with transactionRequest as below:

    func prepareConfig(type: PaymentMethod) -> TransactionRequest {
    let billingAddress = BillingAddress(city: "VND", countryCode: "VN", locale: "en", line1: "address1", line2: "address2", postalCode: "400202 state: "Mah")
    let billingDetails = BillingDetails(billingName: "Test mark", billingEmail: "markweins@gmail.com", billingPhone: "+918341469169 billingAddress: billingAddress )
    let shippingAddress = ShippingAddress(city: "abc", countryCode: "VN", locale: "en", line1: "address_1", line2: "address_2", postalCode"400202", state: "Mah")
    let shippingDetails = ShippingDetails(shippingName: "xyz", shippingEmail: "xyz@gmail.com", shippingPhone: "1234567890", shippingAddressshippingAddress)
    let orderDetails = OrderDetails(id: "knb", name: "kim nguyen bao", price: 1000, quantity: 1)
    return TransactionRequest(
    chaipayKey: "lzrYFPfyMLROallZ",
    paymentChannel: type.paymentMethod,
    paymentMethod: type.paymentMethod,
    merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))",
    amount: 180000,
    currency: "VND",
    signatureHash: "123",
    billingAddress: billingDetails,
    shippingAddress: shippingDetails,
    orderDetails: [orderDetails],
    successURL: "chaipay://",
    failureURL: "chaipay://",
    redirectURL: "chaipay://" )
    }
  • Pass the TransactionRequest to initiateWalletPayments as below:

    let config = prepareConfig(type: .ZaloPay)
    checkOut?.initiateWalletPayments(config) { result in
    switch result {
    case .success(let data):
    // Do nothing
    case .failure(let error):
    // Handle the error part
    print("error", error)
    break
    }
    }

    Handle the success and failure cases from the delegate method as below:

    extension ViewController: CheckoutDelegate {
    func transactionResponse(_ webViewResponse: WebViewResponse?) {
    if let response = webViewResponse {
    //Todo: Polulate date or do the needful
    }
    }
    }

    Sample Payload request:

    var payload = {
    chaipayKey: "lzrYFPfyMLROallZ",
    paymentChannel: ZALOPAY,
    paymentMethod: ZALOPAY_WALLET,
    merchantOrderId: 'MERCHANT1628666326578',
    amount: 4499999,
    currency: "VND",
    signature_hash:"+qGV+YhWEituu7Cf0coqdEgLtcH6qWKXXMDLI2jhxQU=",
    orderDetails: [
    {
    "id": "knb",
    "name": "kim nguyen bao",
    "price": 4499999,
    "quantity": 1
    }
    ],
    billingAddress: {
    billing_name: "Test mark",
    billing_email: "markweins@gmail.com",
    billing_phone: "+919998878788",
    billing_address: {
    city: "VND",
    country_code: "VN",
    locale: "en",
    line_1: "address",
    line_2: "address_2",
    postal_code: "400202",
    state: "Mah",
    },
    },
    shippingAddress: {
    shipping_name: "xyz",
    shipping_email: "xyz@gmail.com",
    shipping_phone: "1234567890",
    shipping_address: {
    city: "abc",
    country_code: "VN",
    locale: "en",
    line_1: "address_1",
    line_2: "address_2",
    postal_code: "400202",
    state: "Mah",
    },
    },
    };

    Sample response#

    Success callback :

    {
    "chaipay_order_ref": "1wc00XMK4uKy3EeYBAvRPlkfjkZ",
    "channel_order_ref": "210812000000171",
    "merchant_order_ref": "MERCHANT1628742344516",
    "status": "Success",
    "status_code": "2000",
    "status_reason": "SUCCESS"
    }

    Failure Callback:

    {
    "chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf",
    "channel_order_ref": "0",
    "merchant_order_ref": "MERCHANT1628681469666",
    "status": "Initiated",
    "status_code": "4000",
    "status_reason": "INVALID_TRANSACTION_ERROR"
    }

    Steps for Signature Hash GenerationPayment Request

    Initiate with new credit card Payment

    • Initialise the new card payment with transactionRequest as below:

      func prepareConfig(type: PaymentMethod) -> TransactionRequest {
      let billingAddress = BillingAddress(city: "VND", countryCode: "VN", locale: "en", line1: "address1", line2: "address2", postalCode: "400202", state: "Mah")
      let billingDetails = BillingDetails(billingName: "Test mark", billingEmail: "markweins@gmail.com", billingPhone: "+918341469169", billingAddress: billingAddress )
      let shippingAddress = ShippingAddress(city: "abc", countryCode: "VN", locale: "en", line1: "address_1", line2: "address_2", postalCode: "400202", state: "Mah")
      let shippingDetails = ShippingDetails(shippingName: "xyz", shippingEmail: "xyz@gmail.com", shippingPhone: "1234567890", shippingAddress: shippingAddress)
      let orderDetails = OrderDetails(id: "knb", name: "kim nguyen bao", price: 1000, quantity: 1)
      return TransactionRequest(
      chaipayKey: "lzrYFPfyMLROallZ",
      paymentChannel: type.paymentMethod, //"MASTERCARD"
      paymentMethod: type.paymentMethod, //"MASTERCARD_CARD"
      merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))",
      amount: 180000,
      currency: "VND",
      signatureHash: "123",
      billingAddress: billingDetails,
      shippingAddress: shippingDetails,
      orderDetails: [orderDetails],
      successURL: "chaipay://",
      failureURL: "chaipay://",
      redirectURL: "chaipay://" )
      }
    • cardDetails:

      let cardDetails = CardDetails(token: nil, cardNumber: "5111111111111118", expiryMonth: "05", expiryYear: "2021", cardHolderName: "NGUYEN VAN A", type: "visa", cvv: "100")
      let config = prepareConfig(type: PaymentMethod.NewCreditCard
    • Pass the TransactionRequest and cardDetails to initiateNewCardPayment as below:

      let config = prepareConfig(type: .NewCreditCard)
      checkOut?.initiateNewCardPayment(config, cardDetails: cardDetails) { [weak self] result in
      guard let self = self else {
      return
      }
      switch result {
      case .success(let response):
      // Do the needful with the response
      case .failure(let error):
      print(error)
      // Handle the error cases
      }
      }

      Handle the success and failure cases from the delegate method as below:

      extension ViewController: CheckoutDelegate {
      func transactionResponse(_ webViewResponse: WebViewResponse?) {
      if let response = webViewResponse {
      //Todo: Polulate date or do the needful
      }
      }
      }

      Sample Payload request:

      var payload = {
      chaipayKey: "lzrYFPfyMLROallZ",
      paymentChannel: MASTERCARD,
      paymentMethod: MASTERCARD_CARD,
      merchantOrderId: 'MERCHANT1628666326578',
      amount: 4499999,
      currency: "VND",
      signature_hash:"+qGV+YhWEituu7Cf0coqdEgLtcH6qWKXXMDLI2jhxQU=",
      orderDetails: [
      {
      "id": "knb",
      "name": "kim nguyen bao",
      "price": 4499999,
      "quantity": 1
      }
      ],
      billingAddress: {
      billing_name: "Test mark",
      billing_email: "markweins@gmail.com",
      billing_phone: "+919998878788",
      billing_address: {
      city: "VND",
      country_code: "VN",
      locale: "en",
      line_1: "address",
      line_2: "address_2",
      postal_code: "400202",
      state: "Mah",
      },
      },
      shippingAddress: {
      shipping_name: "xyz",
      shipping_email: "xyz@gmail.com",
      shipping_phone: "1234567890",
      shipping_address: {
      city: "abc",
      country_code: "VN",
      locale: "en",
      line_1: "address_1",
      line_2: "address_2",
      postal_code: "400202",
      state: "Mah",
      },
      },
      };

      Sample response#

      *Sample Success callback :*

      {
      "merchant_order_ref" : "MERCHANT1630665361511",
      "message" : "",
      "is_success" : "true",
      "order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG",
      "deep_link" : "",
      "channel_order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG",
      "additional_data" : null,
      "redirect_url" : ""
      }

      *Sample Failure Callback*:

      {
      "chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf",
      "channel_order_ref": "0",
      "merchant_order_ref": "MERCHANT1628681469666",
      "status": "Initiated",
      "status_code": "4000",
      "status_reason": "INVALID_TRANSACTION_ERROR"
      }

      Steps for Signature Hash GenerationPayment Request

      Initiate with Saved credit card Payment#

    • Initialise the saved card payment with transactionRequest as below:

      func prepareConfig(type: PaymentMethod) -> TransactionRequest {
      let billingAddress = BillingAddress(city: "VND", countryCode: "VN", locale: "en", line1: "address1", line2: "address2", postalCode: "400202", state: "Mah")
      let billingDetails = BillingDetails(billingName: "Test mark", billingEmail: "markweins@gmail.com", billingPhone: "+918341469169", billingAddress: billingAddress )
      let shippingAddress = ShippingAddress(city: "abc", countryCode: "VN", locale: "en", line1: "address_1", line2: "address_2", postalCode: "400202", state: "Mah")
      let shippingDetails = ShippingDetails(shippingName: "xyz", shippingEmail: "xyz@gmail.com", shippingPhone: "1234567890", shippingAddress: shippingAddress)
      let orderDetails = OrderDetails(id: "knb", name: "kim nguyen bao", price: 1000, quantity: 1)
      return TransactionRequest(
      chaipayKey: "lzrYFPfyMLROallZ",
      paymentChannel: type.paymentMethod, //"MASTERCARD"
      paymentMethod: type.paymentMethod, //"MASTERCARD_CARD"
      merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))",
      amount: 180000,
      currency: "VND",
      signatureHash: "123",
      billingAddress: billingDetails,
      shippingAddress: shippingDetails,
      orderDetails: [orderDetails],
      successURL: "chaipay://",
      failureURL: "chaipay://",
      redirectURL: "chaipay://" )
      }
    • cardDetails:

      let cardDetails = CardDetails(token: savedCard.token, cardNumber: savedCard.partialCardNumber, expiryMonth: savedCard.expiryMonth, expiryYear: savedCard.expiryYear, cardHolderName: "", type: savedCard.type, cvv: "100")
      let config = prepareConfig(type: PaymentMethod.NewCreditCard)
    • Pass the TransactionRequest and cardDetails to initiateNewCardPayment as below:

      let config = prepareConfig(type: .SavedCard)
      checkOut?.initiateNewCardPayment(config, cardDetails: cardDetails) { [weak self] result in
      guard let self = self else {
      return
      }
      switch result {
      case .success(let response):
      // Do the needful with the response
      case .failure(let error):
      print(error)
      // Handle the error cases
      }
      }

      Handle the success and failure cases from the delegate method as below:

      extension ViewController: CheckoutDelegate {
      func transactionResponse(_ webViewResponse: WebViewResponse?) {
      if let response = webViewResponse {
      //Todo: Polulate date or do the needful
      }
      }
      }

      Sample Payload request:

      var payload = {
      chaipayKey: "lzrYFPfyMLROallZ",
      paymentChannel: MASTERCARD,
      paymentMethod: MASTERCARD_CARD,
      merchantOrderId: 'MERCHANT1628666326578',
      amount: 4499999,
      currency: "VND",
      signature_hash:"+qGV+YhWEituu7Cf0coqdEgLtcH6qWKXXMDLI2jhxQU=",
      orderDetails: [
      {
      "id": "knb",
      "name": "kim nguyen bao",
      "price": 4499999,
      "quantity": 1
      }
      ],
      billingAddress: {
      billing_name: "Test mark",
      billing_email: "markweins@gmail.com",
      billing_phone: "+919998878788",
      billing_address: {
      city: "VND",
      country_code: "VN",
      locale: "en",
      line_1: "address",
      line_2: "address_2",
      postal_code: "400202",
      state: "Mah",
      },
      },
      shippingAddress: {
      shipping_name: "xyz",
      shipping_email: "xyz@gmail.com",
      shipping_phone: "1234567890",
      shipping_address: {
      city: "abc",
      country_code: "VN",
      locale: "en",
      line_1: "address_1",
      line_2: "address_2",
      postal_code: "400202",
      state: "Mah",
      },
      },
      };

      Sample response#

      *Sample Success callback :*

      {
      "merchant_order_ref" : "MERCHANT1630665361511",
      "message" : "",
      "is_success" : "true",
      "order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG",
      "deep_link" : "",
      "channel_order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG",
      "additional_data" : null,
      "redirect_url" : ""
      }

      *Sample Failure Callback*:

      {
      "chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf",
      "channel_order_ref": "0",
      "merchant_order_ref": "MERCHANT1628681469666",
      "status": "Initiated",
      "status_code": "4000",
      "status_reason": "INVALID_TRANSACTION_ERROR"
      }

      Steps for Signature Hash GenerationPayment Request

      V3 functionality#

      To create the payment link, generate the JWT token

      Generate the JWT token:

      • create the JWT token using the payload and portone secretKey

        • payload:

          var payload = {
          iss: 'CHAIPAY',
          sub: 'lzrYFPfyMLROallZ',
          iat: new Date().getTime(),
          exp: new Date().getTime() + 100 * 1000,
          };
          // Secret key of your domain
          var secretKey =
          '0e94b3232e1bf9ec0e378a58bc27067a86459fc8f94d19f146ea8249455bf242';

          Details

          • Algorithm to use for generating token - “HS256”

          • To get the payment link to use the SDK checkout UI, call the checkOutUI method from SDK as below:

            checkout?.checkOutUI(config: config, jwtToken: token, onCompletionHandler: { (result) in
            switch result{
            case .success(let data):
            print("Data", data)
            case .failure(let error):
            print("error", error)
            }
            })
        • Sample Payload:

          let billingAddress = BillingAddress(city: "VND", countryCode: "VN", locale: "en", line1: "address1", line2: "address2", postalCode: "400202", state: "Mah")
          let merchantDetails = MerchantDetails(name: "Downy", logo: "images/v184_135.png", backUrl: "https://demo.portone.cloud/checkout.html",promoCode: "Downy350", promoDiscount: 0, shippingCharges: 0.0)
          let billingDetails = BillingDetails(billingName: "Test mark", billingEmail: "markweins@gmail.com", billingPhone: "+918341469169",billingAddress: billingAddress)
          let shippingAddress = ShippingAddress(city: "abc", countryCode: "VN", locale: "en", line1: "address_1", line2: "address_2",postalCode: "400202", state: "Mah")
          let shippingDetails = ShippingDetails(shippingName: "xyz", shippingEmail: "xyz@gmail.com", shippingPhone: "1234567890",shippingAddress: shippingAddress)
          var orderDetails: [OrderDetails] = []
          for details in self.selectedProducts {
          let product = OrderDetails(id: details.id ?? "", name: details.title ?? "", price: details.price ?? 0, quantity: 1)
          orderDetails.append(product)
          }
          let transactionRequest = WebTransactionRequest(chaipayKey: "aiHKafKIbsdUJDOb", merchantDetails: merchantDetails, merchantOrderId:"MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))", amount: getTotalAmount(), currency: "VND", signatureHash: "123",billingAddress: billingDetails, shippingAddress: shippingDetails, orderDetails: orderDetails, successURL: "chaipay://", failureURL:"chaipay://", redirectURL: "chaipay://checkout", countryCode: "VN", expiryHours: 2, source: "api", description: "test dec",showShippingDetails: true, showBackButton: false, defaultGuestCheckout: false, isCheckoutEmbed: false )
        • JWT token generation

          func createJWTToken() -> String {
          struct Header: Encodable {
          let alg = "HS256"
          let typ = "JWT"
          }
          func generateCurrentTimeStamp (extraTime: Int = 0) -> Int {
          let currentTimeStamp = Date().timeIntervalSince1970 + TimeInterval(extraTime)
          let token = String(currentTimeStamp)
          return Int(currentTimeStamp)
          }
          struct Payload: Encodable {
          let iss = "CHAIPAY"
          let sub = "aiHKafKIbsdUJDOb"
          let iat = generateCurrentTimeStamp()
          let exp = generateCurrentTimeStamp(extraTime: 10000)
          }
          let secret = "2601efeb4409f7027da9cbe856c9b6b8b25f0de2908bc5322b1b352d0b7eb2f5"
          //let secret = "a3b8281f6f2d3101baf41b8fde56ae7f2558c28133c1e4d477f606537e328440"
          let privateKey = SymmetricKey(data: secret.data(using: .utf8)!)
          let headerJSONData = try! JSONEncoder().encode(Header())
          let headerBase64String = headerJSONData.urlSafeBase64EncodedString()
          let payloadJSONData = try! JSONEncoder().encode(Payload())
          let payloadBase64String = payloadJSONData.urlSafeBase64EncodedString()
          let toSign = (headerBase64String + "." + payloadBase64String).data(using: .utf8)!
          let signature = HMAC<SHA256>.authenticationCode(for: toSign, using: privateKey)
          let signatureBase64String = Data(signature).urlSafeBase64EncodedString()
          let token = [headerBase64String, payloadBase64String, signatureBase64String].joined(separator: ".")
          return token
          }

          Should implement the delegate methods as below to get the response of failure and success callbacks from the webView.

          extension ProductListViewController: CheckoutDelegate {
          var viewController: UIViewController? {
          return self
          }
          func transactionResponse(_ webViewResponse: WebViewResponse?) {
          print("webview response", webViewResponse)
          // Do the needful
          }
          }