Build Your Own Checkout Using iOS
-
This page contains details of the latest version of the PortOne Merchant's Checkout iOS SDK, version 2
-
The PortOne Merchant's Checkout iOS SDK is designed to enable you to add PortOne Checkout services into your native iOS apps quickly and easily.
How to Setup iOS Native SDK
Please follow steps below to setup iOS Native SDK and start accepting payment via PortOne.
-
Download the latest SDK from this link.
-
After downloading the .xcframework inside the SDK folder, drag and drop it in the project.
-
Add the SDK to your application and in your Xcode project, select your target and add PortOnePaymentSDK.xcframework to:
-
In Xcode navigate to General tab and select the Frameworks, Library and Embedded Content section and add or drop the framework and change the Embed to Embed & sign.
-
For acessing the PortOnePaymentSDk at required places, use the command given here to import:
import ChaiPayPaymentSDK
-
-
In AppDelegate, to initialize the checkout instance in didFinishLaunchingWithOptions method to get the available methods in SDK as below:
var checkout = Checkout(delegate: self, environment: "sandbox", redirectURL: "chaiport/checkout") // redirectURL: your app scheme
-
In AppDelegate pass the delegate to the initialization and implement the delegate methods as given below to get the response of failure and success callbacks from the webView.
extension ViewController: CheckoutDelegate {
func transactionResponse(transactionResponse: TransactionResponse?) {
if let response = transactionResponse {
// Do the needful with response
}
}
//For webview to be open on top of the View controller
var viewController: UIViewController? {
return self
}
func transactionErrorResponse(error: Error?) {
print("Error",error)
}
}
- In the info.plist add the new Array type node LSApplicationQueriesSchemes as shown below to open the other applications:
<key>LSApplicationQueriesSchemes</key> <array> <string>itms-appss</string> <string>momo</string> <string>zalopay</string> <string>chaipay</string> </array>
- Include the URLType in info.plist to redirect it to your app(deep linking) as given below
<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>
How to Handle Deep Link
To handle the deep link follow the steps given below:
-
In sceneDelegate, redirect the deep link url to checkout instance
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { if let url = URLContexts.first?.url, url.absoluteString.hasPrefix("chaiport:") { // call the handle url method from checkout instance AppDelegate.shared.checkout?.handleUrl(url: url) } }
Initiate Payments
Payment flows can be initiated in the following way:
- Initiate payment with tokenization flow:
- New credit card payment
- Saved credit card payment
- Initiate payment with non tokenization flow:
- Wallets, BNPL, net banking
Initiate the Wallet Payment
-
Initialize the wallet payment with transactionRequest as below:
func prepareConfig(type: PaymentMethod) -> TransactionRequest { let countryCode = UserDefaults.getCurrency.code let billingDetails = BillingDetails(billingName: "Test mark", billingEmail: "[email protected]", billingPhone: number ?? "+66900002001", billingAddress: getBillingadress()) let shippingAddress = ShippingAddress(city: "abc", countryCode: "TH", locale: "en", line1: "address_1", line2: "address_2", postalCode: "400202", state: "Mah") let shippingDetails = ShippingDetails(shippingName: "xyz", shippingEmail: "[email protected]", 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, imageUrl: details.imageName ?? "") orderDetails.append(product) totalAmount = totalAmount + (details.price ?? 0) } let merchantDetails = MerchantDetails(name: "Downy", logo: "https://upload.wikimedia.org/wikipedia/commons/a/a6/Logo_NIKE.svg", backUrl: "https://demo.chaiport.io/checkout.html", promoCode: "Downy350", promoDiscount: 35000, shippingCharges: 0.0) print("UserDefaults.getTransactionType.code",UserDefaults.getTransactionType.code) var transactionRequest = TransactionRequest(portOneKey: chaipayKey , key: chaipayKey , merchantDetails: merchantDetails, paymentChannel: selectedPaymentMethod?.paymentChannelKey ?? "", paymentMethod: selectedPaymentMethod?.paymentChannelKey == "VNPAY" ? "VNPAY_ALL" : selectedPaymentMethod?.paymentMethodKey ?? "", merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))", amount: Int(self.totalAmount), currency: countryCode, signatureHash: "123", billingAddress: billingDetails, shippingAddress: shippingDetails, orderDetails: orderDetails, successURL: "https://test-checkout.chaiport.io/success.html", failureURL: "https://test-checkout.chaiport.io/failure.html", redirectURL: "chaiport://checkout", countryCode: countryCode, routingEnabled: false, routingParams: nil, transactionType: UserDefaults.getTransactionType.code) let signatureHash = createSignatureHash(transactionRequest) transactionRequest.signatureHash = signatureHash return transactionRequest }
-
Pass the TransactionRequest to initiateWalletPayments as below:
// payload with proper payment channel and method let config = prepareConfig() checkOut?.initiatePayment(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(_ transactionResponse: TransactionResponse?) {
if let response = transactionResponse {
//Todo: Populate date or do the needful
}
}
}
Sample Response
{
"is_success": true,
"redirect_url": "https://api.omise.co/payments/paym_test_5vs49nt75qk2i3jbkvi/authorize",
"channel_order_ref": "chrg_test_5vs49nqfr5bqq2er4ae",
"merchant_order_ref": "MERCHANT1683899406422",
"order_ref": "2PpGPD8ccxL5thPjMwwPN6KaNMc",
"message": "3DS is forced for this transaction",
"deep_link": "",
"additional_data": null
}
{
"chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf",
"channel_order_ref": "0",
"merchant_order_ref": "MERCHANT1628681469666",
"status": "Initiated",
"status_code": "4000",
"status_reason": "INVALID_TRANSACTION_ERROR"
}
Initiate New Credit Card Payment
Initialize the new card payment with transactionRequest as shown 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: "[email protected]", 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: "[email protected]", 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://" )
}
-
Implement the New card Details as shown below:
let cardDetails = CardDetails(token: nil, key: UserDefaults.getChaipayKey!, cardNumber: cardNumber, expiryMonth: expiryMonth, expiryYear: expiryYear, cardHolderName: cardHolderName, type: cardType, cvv: cvv) let config = prepareConfig()
-
Pass the TransactionRequest and cardDetails to initiateNewCardPayment as given below:
let config = prepareConfig(type: .NewCreditCard) checkOut?.initiateNewCardPayment(config, cardDetails: cardDetails, jwtToken: jwtToken, clientKey: UserDefaults.getChaipayKey!, onCompletionHandler: { [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 shown below:
extension ViewController: CheckoutDelegate { func transactionResponse(_ transactionResponse: TransactionResponse?) { if let response = webViewResponse { //Todo: Polulate date or do the needful } } }
-
Sample success and failure callback:
{ "merchant_order_ref" : "MERCHANT1630665361511", "message" : "", "is_success" : "true", "order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG", "deep_link" : "", "channel_order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG", "additional_data" : null, "redirect_url" : "" }
{ "chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf", "channel_order_ref": "0", "merchant_order_ref": "MERCHANT1628681469666", "status": "Initiated", "status_code": "4000", "status_reason": "INVALID_TRANSACTION_ERROR" }
Initiate with Saved Credit Card Payment
Initialize the saved card payment with transactionRequest as shown 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: "[email protected]", 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: "[email protected]", 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://" )
}
-
Implement the Saved card details as shown below:
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 initiateSavedCardPayment as shown below:
let config = prepareConfig(type: .SavedCard) checkout?.initiateSavedCardPayment(config: config, cardDetails: cardDetails, onCompletionHandler: { 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 shown below:
extension ViewController: CheckoutDelegate { func transactionResponse(_ transactionResponse: TransactionResponse?) { if let response = webViewResponse { //Todo: Populate date or do the needful } } }
-
Sample Success and failure callback for saved credit card Payment:
{ "merchant_order_ref" : "MERCHANT1630665361511", "message" : "", "is_success" : "true", "order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG", "deep_link" : "", "channel_order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG", "additional_data" : null, "redirect_url" : "" }
{ "chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf", "channel_order_ref": "0", "merchant_order_ref": "MERCHANT1628681469666", "status": "Initiated", "status_code": "4000", "status_reason": "INVALID_TRANSACTION_ERROR" }
How to Fetch Saved Cards for a Particular Number
Capture the mobile number and OTP to fetch the saved credit cards for a particular user and follow the steps given below.
- To generate the OTP, call the method as shown 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 } }
- Sample Response
{ "additional_details": { "MessageId": "ae348f19-3522-5690-a981-d3c531b6066d", "SequenceNumber": null }, "status_code": "2000", "status_reason": "SUCCESS" }
Response code description:
Parameter | Data Type | Description |
---|---|---|
status_code | String | provides the code based on the transaction status (e.g: "2000", "3000") |
status_reason | String | provides the reason for failure |
additional_details | Object | provides additional Information like message Id, SequenceNumber |
-
After receiving the OTP to the given mobile number, the captured mobile number and OTP should pass to the fetchSavedCards as shown below to fetch the saved credit cards for the particular number.
checkOut?.fetchSavedCards(portOneKey: UserDefaults.getChaipayKey!, mobileNumber, otp: OTP, token: nil, onCompletionHandler: { (result) in switch result { case .success(let data): //Do the needful case .failure(let error): // handle the error cases print("errror", error) } }
Parameter Description:
Parameter | Data Type | Description |
---|---|---|
portOneKey * | String | pass the portOne key |
formattedText * | String | Contains mobile number with Country code. (eg: +16625655248 , +918341234123) |
OTP * | String | OTP received to the given mobile number |
token * | String | Token received from this api, if token is passed. then we can skip entering the otp part until the token is expiry. |
Note:(*) marked parameters are mandatory.
Sample Response
{
"content": [
{
"token": "97daee740bb84a6d907dfe46fca1139e",
"partial_card_number": "4242 4242 4242 4242",
"expiry_month": "09",
"expiry_year": "2034",
"type": "visa",
"payment_channel_token": {
"OMISE_CUSTOMER_TOKEN": "cust_test_5va5euv4r4cf68gwcmm",
"STRIPE_CHANNEL_TOKEN": "pm_1Mqyk7CzOuxzGzz2njn9bFFE",
"STRIPE_CUSTOMER_TOKEN": "cus_NcDA43aAVrjiMp"
}
},
{
"token": "fb104ae5e67f48dc96119e24a15382e5",
"partial_card_number": "4242 4242 4242 4242",
"expiry_month": "05",
"expiry_year": "2043",
"type": "visa",
"payment_channel_token": {
"OMISE_CUSTOMER_TOKEN": "cust_test_5va5bi1s74v1lx83p1c",
"STRIPE_CHANNEL_TOKEN": "pm_1N6x36CyiCSpXZcy4wCTC97z",
"STRIPE_CUSTOMER_TOKEN": "cus_NsiUmQBa5K0skZ"
}
}
],
"status_code": "2000",
"status_reason": "SUCCESS"
}
{
"message": "Invalid JWT Token / Client Key. Error in parsing JWT Token.",
"status_code": "4010",
"status_reason": "INVALID_UNAUTHORISED_TRANSACTION_ERROR"
}
Response Description
Parameter | Data Type | Description |
---|---|---|
status_code | String | provides the code based on the transaction status (e.g: "2000", "3000") |
status_reason | String | provides the reason for failure |
additional_details | Object | provides additional Information like message Id, SequenceNumber |
How to get available payment methods
Get the available payment methods with portOne key as shown below:
checkout?.getAvailablePaymentGateways(portOneKey: UserDefaults.getChaipayKey!, currency: UserDefaults.getCurrency.code ,completionHandler: { [weak self] result in
print("result", result)
guard let self = self else { return }
switch result {
case .success(let response):
// Do the needful
return
case .failure(let error):
// Do the needful
break
}
})
Parameter Description
Parameter | Data Type | Description |
---|---|---|
portOneKey | string | pass the portOne key |
currency | string | currency. (eg: VND, THB) |
Sample Response for Success and failure case is shown below:
{
"ALL": [],
"BANK_TRANSFER": [],
"BNPL": [
{
"payment_channel_key": "ATOME",
"payment_method_key": "ATOME_BNPL",
"sub_type": "BNPL",
"logo": "https://chaiport-pg-icons-latest-nov.s3.ap-southeast-1.amazonaws.com/atome_short.png",
"display_name": "Atome",
"is_default": false,
"is_enabled": true,
"is_merchant_sponsored": false,
"collect_payslip": false,
"tokenization_possible": false,
"name": "Atome",
"country": "TH",
"currency": "THB"
}
],
"CARD": [
{
"payment_channel_key": "STRIPE",
"payment_method_key": "STRIPE_CARD",
"sub_type": "INT_CREDIT_CARD",
"logo": "https://chaiport-pg-icons-latest-nov.s3.ap-southeast-1.amazonaws.com/card.png",
"display_name": "Stripe",
"is_default": true,
"is_enabled": true,
"is_merchant_sponsored": false,
"collect_payslip": false,
"tokenization_possible": true,
"name": "Stripe CreditCard",
"country": "GLOBAL",
"currency": "USD,VND,SGD,THB,INR,PHP,IDR,MYR,AUD,EUR,HKD"
}
],
"COD": [],
"CRYPTO": [],
"DIRECT_BANK_TRANSFER": [],
"DIRECT_DEBIT": [],
"INSTALLMENT": [
{
"payment_channel_key": "OMISE",
"payment_method_key": "OMISE_INSTALLMENT",
"sub_type": "INSTALLMENT",
"logo": "https://chaiport-pg-icons-latest-nov.s3.ap-southeast-1.amazonaws.com/omise_short.png",
"display_name": "Omise",
"is_default": true,
"is_enabled": true,
"is_merchant_sponsored": false,
"collect_payslip": false,
"tokenization_possible": false,
"name": "Omise installment",
"country": "TH",
"currency": "THB"
}
],
"NET_BANKING": [],
"OTC": [],
"QR_CODE": [
{
"payment_channel_key": "OMISE",
"payment_method_key": "OMISE_PROMPTPAY",
"sub_type": "QR_CODE",
"logo": "https://chaiport-pg-icons-latest-nov.s3.ap-southeast-1.amazonaws.com/promptpay_short.png",
"display_name": "PromptPay",
"is_default": false,
"is_enabled": true,
"is_merchant_sponsored": false,
"collect_payslip": false,
"tokenization_possible": false,
"name": "PromptPay via Omise",
"country": "TH",
"currency": "THB"
}
],
"VA_BANK_TRANSFER": [],
"WALLET": [
{
"payment_channel_key": "OMISE",
"payment_method_key": "OMISE_RABBIT_LINEPAY",
"sub_type": "WALLET",
"logo": "https://chaiport-pg-icons-latest-nov.s3.ap-southeast-1.amazonaws.com/linepay_short1.png",
"display_name": "Rabbit LinePay",
"is_default": false,
"is_enabled": true,
"is_merchant_sponsored": false,
"collect_payslip": false,
"tokenization_possible": false,
"name": "RabbitLinepay via Omise",
"country": "TH",
"currency": "THB"
}
]
}
{
"message": "Invalid JWT Token / Client Key. Error in parsing JWT Token.",
"status_code": "4010",
"status_reason": "INVALID_UNAUTHORISED_TRANSACTION_ERROR"
}
Response Description
Parameter | Data Type | Description |
---|---|---|
status_code | String | provides the code based on the transaction status (e.g: "2000", "3000") |
status_reason | String | provides the reason for failure |
message | String | provides user friendly message for the reason of failure |
Pre-Auth Transaction
Pass the transaction-type value in payload as PREAUTH to make a transaction without capture
- transaction-type values can be following:
- PURCHASE - for normal transaction
- PREAUTH - for without capture transactions
- To capture the transaction, call the captureTransactionAPI from the sdk with portOneOrderRef , portOnekey, jwtToken as the params.
checkout?.captureTransactionAPI(transactionOrderRef: self.transactionId ?? "", clientKey: UserDefaults.getChaipayKey!, jwtToken: jwtToken) { result in switch result { case .success(let response): // Do the needful return case .failure(let error): // Do the needful break } }
Parameter Description
Parameter | Data Type | Description |
---|---|---|
transactionOrderRef * | String | Transaction order Id |
clientKey * | String | PortOne key |
Note: (*) marked are mandatory parameters.
Failover Routing
For failover routing, need to pass the params of routingEnabled and routingParams in the payload while doing a transaction.
Parameter Description
Parameter | Data Type | Description |
---|---|---|
routingEnabled * | Boolean | true / false (if true, then it will capture as a failover routing) |
routingParams ** | Object | type: "failover", route_ref: String(capture from merchant portal or via fetch routes API) |
Note:(*) marked are mandatory parameters. (**) marked are mandatory parameter if routingEnable
is true.
Fetch Routes with Portone key.
checkout?.fetchRoutes(clientKey: clientKey, jwtToken: token, onCompletionHandler: {(result) in
switch result {
case .success(let data):
//Do the needful
return
case .failure(let error):
// Do the needful
break
}})
Parameter Description
Parameter | Data Type | Description |
---|---|---|
clientKey * | String | PortOne key |
jwtToken * | String | generate token with PortOne key and secret key. |
Note:(*) marked are mandatory parameters.
Merchant Centric Card Vault Methods
- Add customer card
- To add the card data for a customer, need to pass the
customerId
,client key
,jwtToken
and card details to the below methodcheckout?.addCustomerCard(customerId: customerId, clientKey: UserDefaults.getChaipayKey ?? " ", cardData: self.cardDetails, jwtToken: token, onCompletionHandler: { (result) in switch result { case .success(let data): // Do the needful return case.failure(let error): // Do the needful break } })
- To add the card data for a customer, need to pass the
Parameter Description
Parameter | Data Type | Description |
---|---|---|
customerId * | String | Customer ID |
clientKey * | String | PortOne Key |
cardDetails * | Object | Pass the card details (Card Holder Name, Card Number , Expiry Month, Year and CVV) |
jwtToken * | String | Generate token with portone key and secret key. |
Note:(*) marked are mandatory parameters.
Sample Response Code
{
"content": {
"token": "cdec91449d3a4b4bae9144d586a2b972",
"partial_card_number": "424242******4242",
"expiry_month": "07",
"expiry_year": "2031",
"type": "visa",
"payment_channel_token": {}
},
"message": "Card record added successfully for customer!",
"status_code": "2000",
"status_reason": "SUCCESS"
}
{
"message": "Card with given token already exists in the database for this customer",
"status_code": "4001",
"status_reason": "INVALID_PAYMENT_REQUEST"
}
Response Description
Parameter | Data Type | Description |
---|---|---|
message | String | Readable message |
status_reason | String | Status reason |
status_code | String | Provides the code based on the transaction status (e.g: "2000", "3000") |
content | Object | provides the card details with token |
How to Delete Customer Card
To delete the card data for a customer, need to pass the card token, customer id, client key, jwtToken to the below method.
checkout?.deleteCardForCustomerId(customerId: customerId, clientKey: clientKey, jwtToken: createJWTToken(), cardData: DeleteCardDataObject(token: token), onCompletionHandler: { (result) in
switch result {
case .success(let data):
// Do the needful
return
case.failure(let error):
// Do the needful
break
}
})
Parameter Description
Parameter | Data Type | Description |
---|---|---|
customerId * | String | Customer Id |
clientKey * | String | PortOne key |
cardData * | Object | Pass the card token |
jwtToken * | String | Generate token with Portone key and secret key. |
Note:(*) marked are mandatory parameters.
{
"message": "Card record deleted successfully for the customer!",
"status_code": "2000",
"status_reason": "SUCCESS"
}
{
"message": "Customer card not found with the token: cdec91449d3a4b4bae9144d586a2b972",
"status_code": "4016",
"status_reason": "MERCHANT_NOT_FOUND"
}
Response Description
Parameter | Data Type | Description |
---|---|---|
message | String | Readable message |
status_reason | String | Status reason |
status_code | String | Provides the code based on the transaction status (e.g: "2000", "3000") |
How to Fetch All Customer Cards
To fetch all the cards data for a customer, need to pass the customer id
, client key
, jwtToken
to the below method:
checkout?.fetchCustomerCards(customerId: customerId, clientKey: clientKey, jwtToken: token, onCompletionHandler: {(result) in
switch result {
case .success(let data):
// Do the needful
return
case .failure(let error):
// Do the needful
break
}})
Parameter Description
Parameter | Data Type | Description |
---|---|---|
customerId * | String | Customer Id |
clientKey * | String | PortOne key |
jwtToken * | String | Generate token with Portone key and secret key. |
Note:(*) marked are mandatory parameters.
Sample Response Code
{
"content": {
"data": [
{
"token": "a6a868835098431b83fc05edf16a2d81",
"partial_card_number": "424242******4242",
"expiry_month": "09",
"expiry_year": "2043",
"type": "visa",
"payment_channel_token": {}
}
]
},
"message": "List of cards for Customer with UUID: 8e52c57d-9bda-437e-973d-fb4f9756d15f fetched successfully.",
"status_code": "2000",
"status_reason": "SUCCESS"
}
{
"message": "Customer not found with customerUUID: 8e52c57d-9bda-437e-973d-fb4f9756d15",
"status_code": "4016",
"status_reason": "MERCHANT_NOT_FOUND"
}
Response Description
Parameter | Data Type | Description |
---|---|---|
message | String | Readable message |
status_reason | String | Status reason |
status_code | String | Provides the code based on the transaction status (e.g: "2000", "3000") |
content | Object | Provides the list of card details in data object |
data | Array | Array of card details |
token | String | Card token |
partial_card_number | String | Card number |
expiry_month | String | Card expiry month |
expiry_year | String | Card expiry year |
type | String | Card type |
payment_channel_token | Object | Can be ignored (PSP related data) |
Sample Payload Request
var payload = {
"chaipay_key": "FdbbkOFFgGdaocap",
"merchant_details": {
"shipping_charges": 0,
"promo_discount": 35000,
"promo_code": "Downy350",
"name": "Downy",
"back_url": "https:\/\/demo.chaiport.io\/checkout.html",
"logo": "https:\/\/upload.wikimedia.org\/wikipedia\/commons\/a\/a6\/Logo_NIKE.svg"
},
"source": "mobile",
"signature_hash": "gzYEOKt1kv99t6SE8rUVYyDK08GIWsUpZ66MYPVVwGo=",
"redirect_url": "chaiport:\/\/checkout",
"amount": 2100,
"transaction_type": "PURCHASE",
"billing_details": {
"billing_name": "Test mark",
"billing_email": "[email protected]",
"billing_address": {
"locale": "en",
"postal_code": "400202",
"city": "THB",
"line_1": "address1",
"line_2": "address2",
"country_code": "TH",
"state": "Mah"
},
"billing_phone": "+918341469169"
},
"failure_url": "https:\/\/test-checkout.chaiport.io\/failure.html",
"pmt_method": "OMISE_CREDIT_CARD",
"country_code": "THB",
"currency": "THB",
"response_type": "redirect_url_only",
"token_params": {
"card_holder_name": " ",
"cvv": "100",
"expiry_year": "2034",
"partial_card_number": "4242 4242 4242 4242",
"save_card": true,
"expiry_month": "09",
"type": "visa",
"token": "97daee740bb84a6d907dfe46fca1139e",
"key": "FdbbkOFFgGdaocap"
},
"routing_enabled": true,
"success_url": "https:\/\/test-checkout.chaiport.io\/success.html",
"order_details": [
{
"quantity": 1,
"id": "H4E354",
"name": "Sririri Toes",
"price": 2100,
"image": "https:\/\/demo.chaiport.io\/images\/bella-toes.jpg"
}
],
"pmt_channel": "OMISE",
"environment": "sandbox",
"routing_params": {
"type": "failover",
"route_ref": "Route_2PalH1cb7sHbV25ZQR11zc0g6yO"
},
"shipping_details": {
"shipping_name": "xyz",
"shipping_phone": "1234567890",
"shipping_email": "[email protected]",
"shipping_address": {
"locale": "en",
"postal_code": "400202",
"city": "abc",
"line_1": "address_1",
"line_2": "address_2",
"country_code": "TH",
"state": "Mah"
}
},
"merchant_order_id": "MERCHANT1683899406422"
}
Sample Response
{
"merchant_order_ref" : "MERCHANT1630665361511",
"message" : "",
"is_success" : "true",
"order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG",
"deep_link" : "",
"channel_order_ref" : "1xcrkpVPNq5vuqQDe3eqrHD3OcG",
"additional_data" : null,
"redirect_url" : ""
}
{
"chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf",
"channel_order_ref": "0",
"merchant_order_ref": "MERCHANT1628681469666",
"status": "Failed",
"status_code": "4000",
"status_reason": "INVALID_TRANSACTION_ERROR"
}
Steps for Signature Hash Generation
Sample creation of signature hash. Follow the steps from the above link for more understanding.
func createSignatureHash(_ config: TransactionRequest) -> String {
var message = ""
message =
"amount=\(config.amount)" +
"&client_key=\(config.portOneKey.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")" +
"¤cy=\(config.currency!.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")" +
"&failure_url=\(config.failureURL!.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")" +
"&merchant_order_id=\(config.merchantOrderId.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")" +
"&success_url=\(config.successURL!.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")"
let secretString = secretKey
let key = SymmetricKey(data: secretString.data(using: .utf8)!)
let signature = HMAC<SHA256>.authenticationCode(for: message.data(using: .utf8)!, using: key)
let base64 = Data(signature).toBase64String()
return base64
}
Steps for generating JWT Token Generation
Sample creation of JWT token. Follow the steps from the above link for more understanding.
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)
}
let payload = Payload(iss: "CHAIPAY", sub: UserDefaults.getChaipayKey! ?? "", iat: generateCurrentTimeStamp(), exp: generateCurrentTimeStamp(extraTime: 1000000))
print("UserDefaults.getChaipayKey",UserDefaults.getChaipayKey)
let secret = UserDefaults.getSecretKey!
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
}
Updated about 2 months ago