Radial Integration | 2016
This page contains samples of code in iOS apps that integrate with Radial payment APIs.
The following technologies (both software and hardware) were used in order to do end-to-end testing for this iOS app
/*
* This function is invoked when the user taps on the "Pay with ApplePay Button".
* This function will show the ApplePay Payment Sheet.
* PKPaymentAuthorizationViewController is a class from the PassKit framework which we have
* imported into this class.
* The main purpose of the controller is to show the ApplePay Payment Sheet.
* You can change what tender types are allowed for display in the sheet by changing the
* supportedNetworks attribute in the PKPaymentRequest object
* that is passed to this controller.
* We are setting the delegate attribute of PKPaymentAuthorizationViewController object to
* self, which means that PKPaymentAuthorizationViewController
* object will look for a PKPaymentAUthorizationViewControllerDelegate implementation in
* this class. The various functions in this delegate implementation
* will get called at the various stages of the payment processing.
* Finally the presentViewController call will show the Applepay Payment Sheet with the
* various payment options.
*/
@IBAction func purchase(sender: UIButton) {
// TODO: - Fill in implementation
let request = PKPaymentRequest()
request.merchantIdentifier = ApplePaySwagMerchantId
request.supportedNetworks = SupportedPaymentNetworks
request.merchantCapabilities = PKMerchantCapability.Capability3DS
request.countryCode = "US"
request.currencyCode = "USD"
var summaryItems = [PKPaymentSummaryItem]()
summaryItems.append(PKPaymentSummaryItem(label: swag.title, amount: swag.price))
if (swag.swagType == .Delivered) {
summaryItems.append(PKPaymentSummaryItem(label: "Shipping", amount: swag.shippingPrice))
}
summaryItems.append(PKPaymentSummaryItem(label:"Apple Pay Demo", amount: swag.total()))
request.paymentSummaryItems = summaryItems
request.requiredBillingAddressFields = PKAddressField.All
switch(swag.swagType) {
case SwagType.Delivered:
request.requiredShippingAddressFields = [PKAddressField.PostalAddress,PKAddressField.Phone]
case SwagType.Electronic:
request.requiredShippingAddressFields = PKAddressField.Email
}
let applePayController = PKPaymentAuthorizationViewController(paymentRequest: request)
applePayController.delegate = self
self.presentViewController(applePayController, animated: true, completion: nil)
}
/**
* Below class is an implementation of the PKPaymentAuthorizationViewControllerDelegate.
* The methods of this class get invoked when the apple pay authorization starts and finishes
* When the user has authorized the payment with touchId, the secure element on the
* mobile device retrieves the associated payment information and passes it to the
* Apple Pay Server. In the server, the DPAN is encrypted with the merchant certificate.
* The merchant Id is an input passed here to look up the merchant certificate.
* The encrypted blob is received by the passkit API which then calls the below
* delegate class's paymentAuthorizationViewController method passing
* the payment information in the PKPayment object.
* The PKPayment.token.paymentData object contains the encrypted payment blob.
* Rest of the information is in plain text.
* We pass this encrypted blob to our decryption service using a rest call.
* The result is a decrypted object in json format.
* The function then proceeds to pull out the values from this result
* and construct the payment service credit card auth request using the processPayment method.
*/
extension BuySwagViewController: PKPaymentAuthorizationViewControllerDelegate {
/*
* This is the first method to be called by the passkit framework once the encrypted
* blob is received from the apple server.
* Our implementation of the call back function below is calling payment service to
* decrypt the encrypted blob.
* It then proceeds to call processPayment method to send this data down to payment
* service for authorization.
*/
func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController,
didAuthorizePayment payment: PKPayment,
completion: (PKPaymentAuthorizationStatus) -> Void) {
let publicApiUrl = "http://lvststwsv04-01.us.gspt.net:1801/public-api-service/
v1.0/stores/SMTUS/payments/decryptblob.xml"
let decryptUrl = NSURL(string: publicApiUrl) // Decryption URL
let decryptRequest = NSMutableURLRequest(URL: decryptUrl!)
decryptRequest.HTTPMethod = "POST"
decryptRequest.setValue("application/xml", forHTTPHeaderField: "Content-Type")
let encryptedPaymentData = payment.token.paymentData
let paymentMethod = payment.token.paymentMethod
let decryptedPaymentData:NSString! = NSString(data: encryptedPaymentData,
encoding: NSUTF8StringEncoding)
//extract values from encrypted JSON string
let decryptedJsonStr = JSON.parse(decryptedPaymentData as String)
var encryptionHeader = "EncryptionHeader"
var version = decryptedJsonStr["version"].stringValue
var data = decryptedJsonStr["data"].stringValue
var signature = decryptedJsonStr["signature"].stringValue
var epk = decryptedJsonStr["header"]["ephemeralPublicKey"].stringValue
var publicKeyHash = decryptedJsonStr["header"]["publicKeyHash"].stringValue
var txId = decryptedJsonStr["header"]["transactionId"].stringValue
let xmlRequest = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>" +
"<DecryptBlobRequest xmlns=\"http://api.gsicommerce.com/schema/checkout/1.0\">" +
"<AlgorithmVersion>pd</AlgorithmVersion>" +
"<EncryptionHeader>" +
"<EphemeralPublicKey>" + epk + "</EphemeralPublicKey>" +
"<TransactionId>" + txId + "</TransactionId>" +
"<PublicKeyHash>" + publicKeyHash + "</PublicKeyHash>" +
"</EncryptionHeader>" +
"<Version>" + version + "</Version>" +
"<Data>" + data + "</Data>" +
"<Signature>" + signature + "</Signature>" +
"</DecryptBlobRequest>"
decryptRequest.HTTPBody = xmlRequest.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(decryptRequest) { data,
response, error in
guard error == nil && data != nil else {
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where
httpStatus.statusCode != 200 {
print("statuscode should be 200, but is \(httpStatus.statusCode)")
} else {
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)!
let shippingAddress = self.createShippingAddressFromRef(
payment.shippingAddress)
let billingAddress = self.createBillingAddressFromRef(payment.billingAddress)
self.processPayment(responseString, shippingAddress: shippingAddress,
billingAddress: billingAddress, completion: completion)
}
}
task.resume()
}
}
/*
* Below function houses the logic for constructing
* credit card auth xml and calls payment service credit card auth api.
*/
func processPayment(ccAuthXmlRequest : NSString, shippingAddress: Address,
billingAddress: Address, completion: (PKPaymentAuthorizationStatus) -> Void) {
do {
let shipFname = shippingAddress.FirstName! as String
let shipLname = shippingAddress.LastName! as String
let shipLine1 = shippingAddress.Street! as String
let shipCity = shippingAddress.City! as String
let shipState = shippingAddress.State! as String
let shipZip = shippingAddress.Zip! as String
var shipCountry = "US"
if shippingAddress.Country != nil {
shipCountry = shippingAddress.Country! as String
}
let amount = self.swag!.total().description
let billFname = billingAddress.FirstName! as String
let billLname = billingAddress.LastName! as String
let billLine1 = billingAddress.Street! as String
let billCity = billingAddress.City! as String
let billState = billingAddress.State! as String
let billZip = billingAddress.Zip! as String
var billCountry = "US"
if billingAddress.Country != nil {
billCountry = billingAddress.Country! as String
}
let visaCCAuthUrl = "http://lvststwsv04-01.us.gspt.net:1801/public-api-service/
v1.0/stores/SMTUS/payments/creditcard/auth/VC.xml"
let ccAuthUrl = NSURL(string: visaCCAuthUrl)
let ccAuthRequest = NSMutableURLRequest(URL: ccAuthUrl!)
ccAuthRequest.HTTPMethod = "POST"
ccAuthRequest.setValue("application/xml", forHTTPHeaderField: "Content-Type")
let text = String(ccAuthXmlRequest)
var dan = ""
var tenderType = ""
var expDate = ""
var txAmount = ""
var onlinePaymentCryptogram = ""
var eci = ""
if let startIndex =
text.rangeOfString("<DeviceAccountNumber isToken=\"false\">")?.endIndex,
let endIndex = text.rangeOfString("</DeviceAccountNumber>")?.startIndex {
dan = text.substringWithRange(startIndex..<endIndex)
}
if let startIndex = text.rangeOfString("<TenderType>")?.endIndex,
let endIndex = text.rangeOfString("</TenderType>")?.startIndex {
tenderType = text.substringWithRange(startIndex..<endIndex)
}
if let startIndex = text.rangeOfString("<ExpirationDate>")?.endIndex,
let endIndex = text.rangeOfString("</ExpirationDate>")?.startIndex {
expDate = text.substringWithRange(startIndex..<endIndex)
}
if let startIndex =
text.rangeOfString("<TransactionAmount currencyCode=\"USD\">")?.endIndex,
let endIndex = text.rangeOfString("</TransactionAmount>")?.startIndex {
txAmount = text.substringWithRange(startIndex..<endIndex)
}
if let startIndex = text.rangeOfString("<OnlinePaymentCryptogram>")?.endIndex,
let endIndex = text.rangeOfString("</OnlinePaymentCryptogram>")?.startIndex {
onlinePaymentCryptogram = text.substringWithRange(startIndex..<endIndex)
}
if let startIndex = text.rangeOfString("<EciIndicator>")?.endIndex,
let endIndex = text.rangeOfString("</EciIndicator>")?.startIndex {
eci = text.substringWithRange(startIndex..<endIndex)
}
let body = ("<CreditCardAuthRequest xmlns=\"http://api.gsicommerce.com/
schema/checkout/1.0\" requestId=\"1234567\">" +
"<PaymentContext>" +
"<OrderId>123456abcd</OrderId>" +
"<PaymentAccountUniqueId isToken=\"false\">" + dan + "</PaymentAccountUniqueId>" +
"</PaymentContext>" +
"<ExpirationDate>" + expDate + "</ExpirationDate>" +
"<CardSecurityCode></CardSecurityCode>" +
"<Amount currencyCode=\"USD\">" + txAmount + "</Amount>" +
"<BillingFirstName>" + billFname + "</BillingFirstName>" +
"<BillingLastName>" + billLname + "</BillingLastName>" +
"<BillingPhoneNo>6101234567</BillingPhoneNo>" +
"<BillingAddress>" +
"<Line1>" + billLine1 + "</Line1>" +
"<Line2></Line2>" +
"<Line3></Line3>" +
"<Line4></Line4>" +
"<City>" + billCity + "</City>" +
"<MainDivision>" + billState + "</MainDivision>" +
"<CountryCode>" + billCountry + "</CountryCode>" +
"<PostalCode>" + billZip + "</PostalCode>" +
"</BillingAddress>" +
"<CustomerEmail>customer@sample.com</CustomerEmail>" +
"<CustomerIPAddress>208.247.73.130</CustomerIPAddress>" +
"<ShipToFirstName>" + shipFname + "</ShipToFirstName>" +
"<ShipToLastName>" + shipLname + "</ShipToLastName>" +
"<ShipToPhoneNo>6101234567</ShipToPhoneNo>" +
"<ShippingAddress>" +
"<Line1>" + shipLine1 + "</Line1>" +
"<Line2></Line2>" +
"<Line3></Line3>" +
"<Line4></Line4>" +
"<City>" + shipCity + "</City>" +
"<MainDivision>" + shipState + "</MainDivision>" +
"<CountryCode>" + shipCountry + "</CountryCode>" +
"<PostalCode>" + shipZip + "</PostalCode>" +
"</ShippingAddress>" +
"<POSMethod>ApplePay</POSMethod>" +
"<isRequestToCorrectCVVOrAVSError>false</isRequestToCorrectCVVOrAVSError>" +
"<SecureVerificationData>" +
"<AuthenticationAvailable>Y</AuthenticationAvailable>" +
"<AuthenticationStatus>Y</AuthenticationStatus>" +
"<CavvUcaf>" + onlinePaymentCryptogram + "</CavvUcaf>" +
"<TransactionId></TransactionId>" +
"<ECI>" + eci + "</ECI>" +
"<PayerAuthenticationResponse<>/PayerAuthenticationResponse>" +
"</SecureVerificationData>" +
"<SchemaVersion>1.1</SchemaVersion>" +
"</CreditCardAuthRequest>")
let requestXmlData = body.dataUsingEncoding(NSUTF8StringEncoding)
ccAuthRequest.HTTPBody = requestXmlData
let task1 = NSURLSession.sharedSession().dataTaskWithRequest(ccAuthRequest) {data,
response, error in
guard error == nil && data != nil else {
print("error=\(error)")
completion(PKPaymentAuthorizationStatus.Failure)
return
}
if let httpStatus = response as? NSHTTPURLResponse
where httpStatus.statusCode != 200 {
print("statuscode should be 200, but is \(httpStatus.statusCode)")
completion(PKPaymentAuthorizationStatus.Failure)
} else {
completion(PKPaymentAuthorizationStatus.Success)
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)!
}
task1.resume()
}
catch let error as NSError {
print (error.localizedDescription)
}
}
Copyright © 2017 Radial. All rights reserved.