Apple Pay Developer Guidelines
1. Танилцуулга
Энэхүү гарын авлага нь Apple Pay-ийг вэб болон аппликейшн дээр нэгтгэх дэлгэрэнгүй заавар бөгөөд домэйн баталгаажуулалт, төлбөр боловсруулалт, аюулгүй байдлын нийцлийг хамарна.
2. Тохиргоо & Нэгтгэл
Бид Apple Pay-ийг e-commerce вэб болон iOS аппликейшнд дэмждэг. Дараах диаграммууд нь Bonum болон Мерчант талын бүх үйлдлүүдийг харуулна:
1. Хэрэв танд вэб аппликейшн нэгтгэл хэрэгтэй бол:
3. Вэб Апп Нэгтгэл
3.1 Домэйн Баталгаажуулалт
Домэйн баталгаажуулалт нь таны вэбсайт дээр Apple Pay-ийг идэвхжүүлэхэд шаардлагатай алхам юм. Энэ нь таныг тухайн домэйны эзэмшигч бөгөөд Apple Pay-ээр төлбөр хүлээн авах эрхтэй гэдгийг батлана.
Эхлэхийн өмнө дараах зүйлсийг хангасан эсэхийг шалгана уу:
- Файл байршуулах вэб сервер эсвэл хостинг орчинд хандах боломж.
- Apple Pay нэгтгэлд ашиглагдах хүчинтэй домэйн нэр.
Алхам 1: Баталгаажуулалтын файлыг авах
- Bonum энэ файлыг өгнө (Файлын нэр: apple-developer-merchantid-domain-association)
Алхам 2: Файлыг вэб серверт байршуулах
1. Шаардлагатай директорыг үүсгэх:
Вэб серверийнхээ үндсэн директорт .well-known нэртэй директор үүсгэнэ үү (хэрэв байхгүй бол).
Жишээ зам:
https://yourdomain.com/.well-known/
2. Файлыг байршуулах:
Татаж авсан
apple-developer-merchantid-domain-association файлыг
.well-known директор дотор байршуулна уу.
Файл нь дараах URL-ээр нийтэд нээлттэй байгаа эсэхийг шалгана уу:
https://yourdomain.com/.well-known/apple-developer-merchantid-domain-association
3. Content-Type:
Apple Pay вэб мерчант баталгаажуулалтын файлыг хөтөч эсвэл HTTP хүсэлтээр хандахад text/plain Content-Type-тай үйлчлэх ёстой.
4. Файлын байршлыг шалгах:
Хөтчийг нээж файлын URL руу очно уу. Файлын агуулга энгийн текст хэлбэрээр харагдах ёстой.
Алхам 3: Бидэнд мэдэгдэх
- Файлыг байршуулсны дараа боломжтой холбоо барих сувгаар бидэнд мэдэгдэнэ үү.
Алхам 4: Apple-ийн домэйн баталгаажуулалт
Манай систем Apple-тай холбогдож таны домэйнийг баталгаажуулна.
Энэ процесс ихэвчлэн хэдхэн минутын дотор дуусна. Баталгаажуулалт амжилттай болсон тухай мерчантуудад мэдэгдэнэ.
Асуудал 1: Файл олдсонгүй
Шалтгаан:
apple-developer-merchantid-domain-association файл
.well-known директорт зөв байршаагүй байна. Шийдэл: Файлын замыг шалгаж, файл URL-ээр хандах боломжтой эсэхийг шалгана уу.
Асуудал 2: Хандалт хориглогдсон
Шалтгаан: Таны сервер
.well-known
директор эсвэл файлд хандалтыг хязгаарласан байна.
Шийдэл: Серверийн зөвшөөрлийг шалгаж,
.well-known директор болон файл нийтэд нээлттэй эсэхийг шалгана уу.
3.2 Вэб Апп Хэрэгжүүлэлт
APPLE PAY > PSP > Web App
- Merchant ID (Merchant ID-г Bonum PSP-ээс өгсөн байх ёстой)
- Merchant Key (Merchant Key-г Bonum PSP-ээс өгсөн байх ёстой)
- Demo Web App Source apple-pay-example-v1.0.0.zip (Эх файлуудыг Bonum PSP-ээс өгсөн байх ёстой)
APPLE PAY > PSP > Demo Web App Source
- index.html - Apple Pay товч болон JavaScript нэгтгэлтэй frontend.
- server.js - Apple Pay мерчант баталгаажуулалт болон төлбөр боловсруулалтыг зохицуулах backend сервер (Node.js Express-тэй).
- package.json - Backend-д шаардлагатай хамаарлуудын жагсаалт (жнь: express, node-fetch).
- package-lock.json - Хамаарлын хувилбаруудыг түгжих автоматаар үүсгэгдсэн файл.
Web App > Frontend Integration
Тэмдэглэл: Apple Pay товчны хэрэгжүүлэлт
Web App > Frontend Integration
Тэмдэглэл:
1. "REPLACE_YOUR_MERCHANT_IDEN_TIFIER_HERE"-г өөрийн merchant ID-аар солино уу.
2. "total" нь төлөх дүнг илэрхийлнэ. Төлбөрийн дэлгэрэнгүй мэдээллийг задлахын тулд "lineItems"-ийг ашиглана уу.
3.
Шинэ Apple Pay session үүсгэнэ.
function startApplePaySession() {
// Merchant-specific information
const merchantIdentifier = 'REPLACE_YOUR_MERCHANT_IDENTIFIER_HERE'; // Replace with your actual merchant ID
// Payment request details
const paymentRequest = {
countryCode: 'MN', // Country code for Mongolia
currencyCode: 'MNT', // Mongolian Tugrik
supportedNetworks: ['visa', 'masterCard', 'amex'], // Supported card networks
merchantCapabilities: ['supports3DS'], // Security capabilities
displayName: 'Your Merchant Name', // Name displayed on the payment sheet
total: {
label: 'Your Merchant Name', // Total summary label
amount: '100', // Total amount (in whole numbers for MNT (100.00 is invalid, 100 - is valid))
},
lineItems: [ //optional
{ label: 'Product A', amount: '200' },
{ label: 'Discount', amount: '-100' },
],
applicationData: btoa(merchantIdentifier), // Base64 encoded merchant ID
};
// Ensure amount is a whole number for Mongolian Tugrik (MNT)
if (paymentRequest.currencyCode === 'MNT' && !Number.isInteger(Number(paymentRequest.total.amount))) {
alert('Amounts in Mongolian Tugrik (MNT) must be whole numbers without decimal points.');
return;
}
// Create a new Apple Pay session
const session = new ApplePaySession(3, paymentRequest);
// Step 3: Validate the merchant during the payment session
session.onvalidatemerchant = async (event) => {
try {
const response = await fetch('https://psp.bonum.mn/api/merchant/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ validationURL: event.validationURL }),
});
const merchantSession = await response.json();
session.completeMerchantValidation(merchantSession);
} catch (error) {
console.error('Merchant validation failed:', error);
session.abort();
}
};
Web App > Frontend Integration
Тэмдэглэл: Төлбөрийн зөвшөөрлийг зохицуулах.
// Step 4: Handle payment authorization
session.onpaymentauthorized = async (event) => {
const token = event.payment.token;
try {
//frontend will send token to its backend. merchant can customize to their needs.
const response = await fetch('http://localhost:3000/api/payment/authorize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, orderId: 'REPLACE_ORDER_ID_HERE' }),
});
const result = await response.json();
// merchant backend response parsing, customize to your needs.
if (result.success) {
session.completePayment(ApplePaySession.STATUS_SUCCESS); //mandatory
///alert("Payment succeeded: $ {result.description}");
} else {
session.completePayment(ApplePaySession.STATUS_FAILURE); //mandatory
// alert("Payment failed: $ {result.description}"");
}
} catch (error) {
console.error('Error processing payment:', error);
session.completePayment(ApplePaySession.STATUS_FAILURE);
alert('Unexpected error occurred while processing payment.');
}
};
// Step 5: Handle payment cancellation
session.oncancel = () => {
console.log('Payment was canceled by the user.');
alert('Payment was canceled.');
};
// Step 6: Begin the session
session.begin();
}
Web App > The backend Integration
Тэмдэглэл: Төлбөрийн зөвшөөрлийг зохицуулж төлбөрийг боловсруулах.
"REPLACE_YOUR_MERCHANT_KEY"-г өөрийн merchant key-ээр солино уу.
const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios');
const cors = require('cors');
const app = express();
const PORT = 3000;
// Allow specific origins (replace "http://example.com" with your frontend's URL)
app.use(cors({
origin: 'https://psp.bonum.mn', // Allow this specific origin
}));
// Middleware: Parses incoming JSON request bodies
app.use(bodyParser.json());
/**
* Endpoint to process payment authorization.
* The client sends a token and an orderId, which are forwarded to the PSP (Payment Service Provider).
*/
app.post('/api/payment/authorize', async (req, res) => {
const { token, orderId } = req.body;
// Step 1: Validate incoming request data
if (!token || !orderId) {
return res.status(400).json({
success: false,
message: 'Missing token or orderId',
});
}
try {
// Step 2: Forward the payment data to the PSP
const response = await axios.post(
'https://psp.bonum.mn/api/payment/process', // PSP endpoint URL
{
token, // Payment token received from Apple Pay
order_id: orderId, // Merchant invoice/order id
},
{
headers: {
'Content-Type': 'application/json',
'x-merchant-key': 'REPLACE_YOUR_MERCHANT_KEY', // Replace with your actual merchant key
},
}
);
console.log('resp', response.data);
// Step 3: Respond to the client with the PSP's response
res.json({
success: response.data.success, // Indicates if the payment was successful
status_code: response.data.status_code, // PSP's status code for the operation
orderId: response.data.orderId, // The order ID provided in the response
description: response.data.desc, // Additional information about the payment
});
} catch (error) {
console.error('Error processing payment:', error.message);
// Step 4: Handle errors from the PSP or network issues
if (error.response) {
// Errors returned directly by the PSP
return res.status(error.response.status).json({
success: error.response.data.success, // Whether the PSP recognized the payment as successful
status_code: error.response.data.status_code, // PSP-specific error code
orderId: error.response.data.orderId, // Order ID (if available)
description: error.response.data.desc, // PSP-provided error description
});
}
// Step 5: Handle unexpected or generic errors (network, server, etc.)
res.status(500).json({
success: false,
status_code: 500,
orderId, // Original order ID for traceability
description: 'Unexpected error occurred while processing payment.', // Generic error message
});
}
});
// Start the Express server
app.listen(PORT, () => {
console.log("Server is running on http://localhost:" +PORT);
});
Web App > The backend integration
Тэмдэглэл: Төлбөр амжилтгүй болсон тохиолдолд /api/payment-log/read API-г ашиглаж болно.
"REPLACE_YOUR_MERCHANT_KEY"-г өөрийн merchant key-ээр солино уу.
Энэ API дуудлагыг таны backend дээр хийх ёстой.
func checkPaymentByOrderId(orderId: String) {
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer = AFJSONResponseSerializer()
manager.responseSerializer.acceptableContentTypes = manager.responseSerializer.acceptableContentTypes?.union(["application/json"])
//TODO: set Merchant Key here. Bonum will provide Merchant Key.
manager.requestSerializer.setValue("SET-YOUR-MERCHANT-KEY-HERE", forHTTPHeaderField: "x-merchant-key")
//Demo URL. Note: This URL charges the bank transaction. Please use small amount when you test
var urlString = "https://testpsp.bonum.mn/api/payment-log/read"
//Production URL. When you push your Build to the production
//var urlString = "https://psp.bonum.mn/api/payment-log/read"
urlString = urlString + "?order_id=" + orderId
// Convert the JSON object to a JSON string
print("API Url: \(urlString)")
manager.get(urlString,
parameters: nil,
headers: nil, progress: { progress in
print("Download progress: \(progress.totalUnitCount)")
}, success: { task, responseObject in
print("Transactions for Order ID: \(orderId)")
print("GET response: \(responseObject ?? "No response")")
}, failure: { task, error in
print("Error on GET request: \(error.localizedDescription)")
})
}
Web App > The backend integration
Тэмдэглэл:
"success": false
нь гүйлгээ ямар нэг шалтгаанаар амжилтгүй болсныг илэрхийлнэ.
Тэмдэглэл:
"success": true
нь гүйлгээ амжилттай боловсруулагдсаныг илэрхийлнэ.
[
{
"merchant_order_id": "9876543",
"amount": 5000,
"success": false,
"createdAt": "2025-01-24T03:22:03.135Z"
},
{
"merchant_order_id": "9876543",
"amount": 5000,
"success": true,
"createdAt": "2025-01-24T03:22:03.135Z"
}
]
4. iOS Апп Нэгтгэл
4.1 Apple Payment Сертификат Үүсгэх
APPLE PAY > PSP > Payment Certificate
1. Apple Developer Account руу очно уу
2. Developer бүртгэлээрээ нэвтэрнэ үү
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
Тэмдэглэл: Таны Merchant ID-г Bonum PSP-ээс имэйлээр илгээсэн. Дэлгэцийн зураг дээрх merchant.portal.mn-г үл тоомсорлоно уу.
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
Merchant ID: merchant.portal.mn
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
Тэмдэглэл: Асуултад "No" гэж сонгоно уу.
APPLE PAY > PSP > Payment Certificate
Хавсаргасан "SigningCertRequest.csr" файлыг байршуулна уу
Тэмдэглэл: .csr файлыг Bonum PSP-ээс имэйлээр илгээсэн.
APPLE PAY > PSP > Payment Certificate
Payment Certificate файлыг татаж аваад бидэнд илгээнэ үү.
4.2 Апп дотор Хэрэгжүүлэлт
APPLE PAY > PSP > In App
1. Үргэлжлүүлэхийн өмнө дараах зүйлсийг бэлдсэн эсэхийг шалгана уу:
- Xcode Project
- Merchant ID (Merchant ID-г Bonum PSP-ээс өгсөн байх ёстой бөгөөд та өгсөн зааврын дагуу Merchant ID-гаа үүсгэсэн байх шаардлагатай.)
- Таны Apple Payment Certificate (Энэ сертификат Bonum PSP серверт амжилттай суулгагдсан байх ёстой.)
- Merchant Key (Merchant Key-г Bonum PSP-ээс өгсөн байх ёстой)
XCODE > Select Target > Signing & Capabilities
XCODE > Select Target > Signing & Capabilities
"Apple Pay" гэж хайна уу
XCODE > Select Target > Signing & Capabilities
Apple Pay идэвхжсэний дараа доорх байдлаар харагдана.
Таны Merchant ID нь
'merchant.your.merchant.id'-ийн оронд харагдах ёстой
XCODE > Select Target > Signing & Capabilities
Таны Merchant ID нь 'merchant.your.merchant.id'-ийн оронд харагдах ёстой.
XCODE > Select Target > ApplePayViewController
Тэмдэглэл: Зааврыг уншиж //TODO: тэмдэглэгдсэн кодыг өөрийн кодоор солино уу.
Код дотор солих чухал зүйлс:
1. "merchant.your.merchant.id"-г өөрийн Merchant ID-аар солино уу.
2. "SET-YOUR-MERCHANT-KEY-HERE"-г өөрийн Merchant Key-ээр солино уу.
3. generateRandomOrderString функцийн оронд захиалгын ID (нэхэмжлэх, SKU гэх мэт) ашиглана уу. Энэ нь аливаа төлбөрийн зүйлийг өвөрмөц байдлаар тодорхойлох боломжийг олгоно. Бид үүнийг гүйлгээний баталгаажуулалтад ашиглана.
ApplePayViewController.swift нь PassKit болон "AFNetworking" ашигладаг.
- PassKit бол Apple-ийн хөгжүүлэгчдэд Apple Pay-г нэгтгэх боломжийг олгодог framework юм
- AFNetworking бол төлбөрийн payload өгөгдлийг Bonum PSP серверт илгээхэд ашиглагддаг нээлттэй эхийн сүлжээний сан юм.
- Таны view controller нь PKPaymentAuthorizationControllerDelegate методуудыг хэрэгжүүлэх ёстой.
XCODE > ApplePayViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
//TODO: Implement PKPaymentButton with its style here.
var button: UIButton?
//Checks whether payment can be made or not.
if PKPaymentAuthorizationController.canMakePayments() {
button = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .black)
button?.addTarget(self, action: #selector(startApplePay), for: .touchUpInside)
} else if PKPaymentAuthorizationController.canMakePayments(usingNetworks: [.masterCard, .visa, .amex]) {
button = PKPaymentButton(paymentButtonType: .setUp, paymentButtonStyle: .black)
button?.addTarget(self, action: #selector(setupPressed(_:)), for: .touchUpInside)
}
if let button = button {
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
XCODE > ApplePayViewController.swift
Тэмдэглэл: PKPaymentSummaryItem дээр дүн болон шошгыг тохируулна уу. Олон бараа зарах нь нэг бараа зарахаас ялгаатай. Код дахь жишээнүүдийг үзнэ үү.
@objc func startApplePay() {
guard PKPaymentAuthorizationController.canMakePayments() else {
print("Apple Pay is not available on this device.")
return
}
guard PKPaymentAuthorizationController.canMakePayments(usingNetworks: [.masterCard, .visa, .amex]) else {
print("Apple Pay is not set up with a supported card.")
return
}
let paymentRequest = PKPaymentRequest()
paymentRequest.merchantIdentifier = "merchant.your.merchant.id" // TODO: Set Your Merchant ID here
paymentRequest.supportedNetworks = [.masterCard, .visa, .amex]
paymentRequest.merchantCapabilities = [.threeDSecure]
paymentRequest.countryCode = "MN" //Country Code of Mongolia
paymentRequest.currencyCode = "MNT" //Currency code of tugrik
//TODO: One-time purchase for multiple items. Replace the label and amount here.
let item1 = PKPaymentSummaryItem(label: "Apple", amount: NSDecimalNumber(string: "5.1"))
let item2 = PKPaymentSummaryItem(label: "Banana", amount: NSDecimalNumber(string: "4.12"))
//TODO: This item represents the total of all items. Its amount should be the sum of all individual items. For example: 5.1 + 4.12 = 9.22.
let total = PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(string: "9.22"))
paymentRequest.paymentSummaryItems = [item1, item2, total]
// TODO: One-time purchase for a single item. Replace the label and price here.
// let total = PKPaymentSummaryItem(label: "Kiwi", amount: NSDecimalNumber(string: "10"))
// paymentRequest.paymentSummaryItems = [total]
let paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
paymentController.delegate = self
paymentController.present { success in
if success {
print("Payment sheet presented successfully.")
} else {
print("Failed to present payment sheet.")
}
}
}
XCODE > ApplePayViewController.swift
Тэмдэглэл:
processPaymentData функц нь гүйлгээг боловсруулна.
convertPKPaymentToJSON функц нь төлбөр боловсруулахад зориулж order_id-тай Payload JSON обьект үүсгэнэ.
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler: @escaping (PKPaymentAuthorizationResult) -> Void) {
print("Payment authorized: \(payment.token)")
print("Payment Network: \(String(describing: payment.token.paymentMethod.network))")
print("Payment Transaction Identifier: \(payment.token.transactionIdentifier)")
processPaymentData(payment) { result in
handler(result)
}
}
func convertPKPaymentToJSON(payment: PKPayment, orderId: String) -> Any? {
// Extract the payment token details
let paymentToken = payment.token
let paymentData = convertToJson(data: paymentToken.paymentData)
let transactionIdentifier = paymentToken.transactionIdentifier
let paymentMethod = paymentToken.paymentMethod
// Extract billing and shipping contacts
let billingContact = payment.billingContact
let shippingContact = payment.shippingContact
// Construct the JSON object
var jsonObject: [String: Any] = [:]
jsonObject["token"] = [
"paymentData": paymentData,
"transactionIdentifier": transactionIdentifier,
"paymentMethod": [
"displayName": paymentMethod.displayName ?? "",
"network": paymentMethod.network?.rawValue ?? "",
"type": paymentMethod.type == .debit ? "debit" :
paymentMethod.type == .credit ? "credit" : "unknown"
],
]
//If unnecessary, don't use
if let billing = billingContact {
jsonObject["billingContact"] = [
"givenName": billing.name?.givenName ?? "",
"familyName": billing.name?.familyName ?? "",
"emailAddress": billing.emailAddress ?? "",
"phoneNumber": billing.phoneNumber?.stringValue ?? ""
]
}
//If unnecessary, don't use
if let shipping = shippingContact {
jsonObject["shippingContact"] = [
"givenName": shipping.name?.givenName ?? "",
"familyName": shipping.name?.familyName ?? "",
"emailAddress": shipping.emailAddress ?? "",
"phoneNumber": shipping.phoneNumber?.stringValue ?? ""
]
}
//Your ORDER ID should be set here
jsonObject["order_id"] = orderId
return jsonObject
}
XCODE > ApplePayViewController.swift
Тэмдэглэл:
processPaymentData функц нь гүйлгээг боловсруулна.
Зөвлөмж: Сервер-Сервер /api/payment/process API дуудлага.
Энэ API дуудлагыг таны backend дээр хийх ёстой.
func processPaymentData(_ payment: PKPayment, completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer = AFJSONResponseSerializer()
manager.responseSerializer.acceptableContentTypes = manager.responseSerializer.acceptableContentTypes?.union(["application/json"])
//TODO: set Merchant Key here. Bonum will provide Merchant Key.
manager.requestSerializer.setValue("SET-YOUR-MERCHANT-KEY-HERE", forHTTPHeaderField: "x-merchant-key")
//TODO: Set Payment process API url here
//Demo URL. Note: This URL charges the bank transaction. Please use small amount when you test
let urlString = "https://testpsp.bonum.mn/api/payment/process"
//Production URL. When you push your Build to the production
//let urlString = "https://psp.bonum.mn/api/payment/process"
//TODO: set your order/invoice/sku/ id here.
let orderId = generateRandomOrderString(length: 20)
orderIdLbl.text = orderId
let jsonPayload = convertPKPaymentToJSON(payment: payment, orderId: orderId)
// Convert the JSON object to a JSON string
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonPayload ?? "", options: .prettyPrinted) {
let jsonString = String(data: jsonData, encoding: .utf8)
print("Payload data: \(jsonString ?? "No response")")
}
manager.post(urlString, parameters: jsonPayload, headers: nil, progress: { progress in
print("Download progress: \(progress.totalUnitCount)")
}, success: { task, responseObject in
print("POST response: \(responseObject ?? "No response")")
// Safely cast responseObject to [String: Any]
if let responseDict = responseObject as? [String: Any] {
// Access the boolean field
if let success = responseDict["success"] as? Bool {
print("Success: \(success)") // Output: Success: true/false
if success {
let successResult = PKPaymentAuthorizationResult(status: .success, errors: nil)
completion(successResult)
} else {
let failureResult = PKPaymentAuthorizationResult(status: .failure, errors: nil)
completion(failureResult)
}
} else {
print("Key 'success' is missing or not a boolean.")
let failureResult = PKPaymentAuthorizationResult(status: .failure, errors: nil)
completion(failureResult)
}
} else {
print("Failed to parse response as dictionary.")
let failureResult = PKPaymentAuthorizationResult(status: .failure, errors: nil)
completion(failureResult)
}
}, failure: { task, error in
print("Error on GET request: \(error.localizedDescription)")
let failureResult = PKPaymentAuthorizationResult(status: .failure, errors: nil)
completion(failureResult)
})
}
XCODE > ApplePayViewController.swift
{
"token":{
"paymentMethod":{
"type":"debit",
"network":"MasterCard",
"displayName":"MasterCard 1588"
},
"transactionIdentifier":"4be88bbec5d887d696b536b87d9a28a25d5ca5e6cbbac6e06bb3c14b0967f16f",
"paymentData":{
"data":"lAGF6hAH+z\/vZz1UG",
"signature":"BkAiA+zmtT6wYi6JxyrpjEqYSYRw82kMhjxXSZIvyd1G9zNgAAAAAAAA==",
"header":{
"publicKeyHash":"EalQwLcwQ0Mqh9R1qWtAwsO4Eht+6T2w9pMNypx2sP8=",
"ephemeralPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzy8Acl6MTWG7luHdWeA\/sI+7mXMczg698cdOB9lYJBVVg2WYNgP4Ila4+vLfqvYQmBurR7wK1UQ2W8Ma2kNUAA==",
"transactionId":"4be88bbec5d887d696b536b87d9a28a25d5ca5e6cbbac6e06bb3c14b0967f16f"
},
"version":"EC_v1"
}
},
"order_id":"1nnuqsc4j6bzpl5hm26z"
}
XCODE > ApplePayViewController.swift
Тэмдэглэл: Төлбөр амжилтгүй болсон тохиолдолд /api/payment-log/read API-г ашиглаж болно.
Зөвлөмж: Сервер-Сервер /api/payment-log/read API дуудлага.
Энэ API дуудлагыг таны backend дээр хийх ёстой.
func checkPaymentByOrderId(orderId: String) {
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer = AFJSONResponseSerializer()
manager.responseSerializer.acceptableContentTypes = manager.responseSerializer.acceptableContentTypes?.union(["application/json"])
//TODO: set Merchant Key here. Bonum will provide Merchant Key.
manager.requestSerializer.setValue("SET-YOUR-MERCHANT-KEY-HERE", forHTTPHeaderField: "x-merchant-key")
//Demo URL. Note: This URL charges the bank transaction. Please use small amount when you test
var urlString = "https://testpsp.bonum.mn/api/payment-log/read"
//Production URL. When you push your Build to the production
//var urlString = "https://psp.bonum.mn/api/payment-log/read"
urlString = urlString + "?order_id=" + orderId
// Convert the JSON object to a JSON string
print("API Url: \(urlString)")
manager.get(urlString,
parameters: nil,
headers: nil, progress: { progress in
print("Download progress: \(progress.totalUnitCount)")
}, success: { task, responseObject in
print("Transactions for Order ID: \(orderId)")
print("GET response: \(responseObject ?? "No response")")
}, failure: { task, error in
print("Error on GET request: \(error.localizedDescription)")
})
}
XCODE > ApplePayViewController.swift
Тэмдэглэл:
"success": false
нь гүйлгээ ямар нэг шалтгаанаар амжилтгүй болсныг илэрхийлнэ.
Тэмдэглэл:
"success": true
нь гүйлгээ амжилттай боловсруулагдсаныг илэрхийлнэ.
[
{
"merchant_order_id": "9876543",
"amount": 5000,
"success": false,
"createdAt": "2025-01-24T03:22:03.135Z"
},
{
"merchant_order_id": "9876543",
"amount": 5000,
"success": true,
"createdAt": "2025-01-24T03:22:03.135Z"
}
]
5. ДЭМЖЛЭГ & ХОЛБОО БАРИХ
Лавлагаа:
Apple Pay on Web Demo by Apple - https://applepaydemo.apple.com
Apple Pay Human Interface Guidelines - https://developer.apple.com/design/human-interface-guidelines/apple-pay
Apple Pay Marketing Resources - https://developer.apple.com/apple-pay/marketing
Apple Pay Marketing Guidelines - https://developer.apple.com/apple-pay/marketing
Нэмэлт тусламж хэрэгтэй бол манай багтай холбогдоно уу:
- И-мэйл:
info@bonum.mn
- Утас: +976 7200-5000
1. Introduction
This guide provides detailed instructions on integrating Apple Pay for web and in-app applications, including domain verification, payment processing, and security compliance.
2. Setup & Integration
We support Apple Pay for e-commerce web and iOS applications.
The following diagrams show all actions on Bonum and Enterprise user
side:
1. If you need web application integration:
3. Web App Integration
3.1 Domain Verification
Domain verification is a required step for enabling Apple Pay on
your website. It confirms that you own the domain and are
authorized to accept payments via Apple Pay.
The process involves placing a special verification file,
provided by us (Bonum PSP), on your domain's web server and
notifying us to verify it with Apple.
Before proceeding, ensure you have:
- Access to your web server or hosting environment to upload
files.
- A valid domain name that matches the one used for your Apple
Pay integration.
Step 1: Obtain the Verification File
- Bonum will provide this file (File name is apple-developer-merchantid-domain-association)
Step 2: Place the File on Your Web Server
1. Create the required directory:
On your web server, create a directory named .well-known in the
root directory of your domain (if it doesn’t already exist).
Example Path:
https://yourdomain.com/.well-known/
2. Upload the file:
Place the downloaded
apple-developer-merchantid-domain-association file inside the
.well-known directory.
Ensure the file is accessible publicly via the following URL:
https://yourdomain.com/.well-known/apple-developer-merchantid-domain-association
3. Content-Type:
The Apple Pay web merchant validation file must be served with a Content-Type of text/plain when accessed via a browser or HTTP request.
4. Verify file placement:
Open a browser and navigate to the file's URL. You should see
the contents of the file displayed as plain text.
Step 3: Notify Us
- After placing the file, let us be informed by any available
communication channels.
Step 4: Domain Verification by Apple
Our system will communicate with Apple to verify your domain.
This process usually completes within a few minutes. We will
inform merchants once the verification is successful.
Issue 1: File Not Found
Cause: The
apple-developer-merchantid-domain-association file is not
correctly placed in the .well-known directory. Solution: Verify
the file path and ensure the file is accessible via its URL.
Issue 2: Access Denied
Cause: Your server has restricted access to the
.well-known
directory or file.
Solution: Check your server's permissions and ensure the
.well-known directory and file are publicly accessible.
3.2 Web App Implementation
APPLE PAY > PSP > Web App
- Merchant ID (The Merchant ID should have been provided by Bonum PSP)
- Merchant Key (The Merchant Key should have been provided by Bonum PSP)
- Demo Web App Source apple-pay-example-v1.0.0.zip (Source files should have been provided by Bonum PSP)
APPLE PAY > PSP > Demo Web App Source
- index.html - The frontend with the Apple Pay button and JavaScript integration.
- server.js - The backend server (Node.js with Express) that handles Apple Pay merchant validation and payment processing.
- package.json - Lists dependencies needed for your backend (e.g., express, node-fetch).
- package-lock.json - Automatically generated to lock dependency versions.
Web App > Frontend Integration
Note: Apple Pay button implementation
Web App > Frontend Integration
Note:
1.Replace
“REPLACE_YOUR_MERCHANT_IDEN_TIFIER_HERE”with your merchant ID.
2. "total” represents the amount to be charged. To break
down the payment details, use "lineItems".
3.
Create a new Apple Pay session.
function startApplePaySession() {
// Merchant-specific information
const merchantIdentifier = 'REPLACE_YOUR_MERCHANT_IDENTIFIER_HERE'; // Replace with your actual merchant ID
// Payment request details
const paymentRequest = {
countryCode: 'MN', // Country code for Mongolia
currencyCode: 'MNT', // Mongolian Tugrik
supportedNetworks: ['visa', 'masterCard', 'amex'], // Supported card networks
merchantCapabilities: ['supports3DS'], // Security capabilities
displayName: 'Your Merchant Name', // Name displayed on the payment sheet
total: {
label: 'Your Merchant Name', // Total summary label
amount: '100', // Total amount (in whole numbers for MNT (100.00 is invalid, 100 - is valid))
},
lineItems: [ //optional
{ label: 'Product A', amount: '200' },
{ label: 'Discount', amount: '-100' },
],
applicationData: btoa(merchantIdentifier), // Base64 encoded merchant ID
};
// Ensure amount is a whole number for Mongolian Tugrik (MNT)
if (paymentRequest.currencyCode === 'MNT' && !Number.isInteger(Number(paymentRequest.total.amount))) {
alert('Amounts in Mongolian Tugrik (MNT) must be whole numbers without decimal points.');
return;
}
// Create a new Apple Pay session
const session = new ApplePaySession(3, paymentRequest);
// Step 3: Validate the merchant during the payment session
session.onvalidatemerchant = async (event) => {
try {
const response = await fetch('https://psp.bonum.mn/api/merchant/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ validationURL: event.validationURL }),
});
const merchantSession = await response.json();
session.completeMerchantValidation(merchantSession);
} catch (error) {
console.error('Merchant validation failed:', error);
session.abort();
}
};
Web App > Frontend Integration
Note: Handle payment authorization.
// Step 4: Handle payment authorization
session.onpaymentauthorized = async (event) => {
const token = event.payment.token;
try {
//frontend will send token to its backend. merchant can customize to their needs.
const response = await fetch('http://localhost:3000/api/payment/authorize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, orderId: 'REPLACE_ORDER_ID_HERE' }),
});
const result = await response.json();
// merchant backend response parsing, customize to your needs.
if (result.success) {
session.completePayment(ApplePaySession.STATUS_SUCCESS); //mandatory
///alert("Payment succeeded: $ {result.description}");
} else {
session.completePayment(ApplePaySession.STATUS_FAILURE); //mandatory
// alert("Payment failed: $ {result.description}"");
}
} catch (error) {
console.error('Error processing payment:', error);
session.completePayment(ApplePaySession.STATUS_FAILURE);
alert('Unexpected error occurred while processing payment.');
}
};
// Step 5: Handle payment cancellation
session.oncancel = () => {
console.log('Payment was canceled by the user.');
alert('Payment was canceled.');
};
// Step 6: Begin the session
session.begin();
}
Web App > The backend Integration
Note: Handle payment authorization and process the payment.
Replace “REPLACE_YOUR_MERCHANT_KEY"with your merchant
key.
const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios');
const cors = require('cors');
const app = express();
const PORT = 3000;
// Allow specific origins (replace "http://example.com" with your frontend's URL)
app.use(cors({
origin: 'https://psp.bonum.mn', // Allow this specific origin
}));
// Middleware: Parses incoming JSON request bodies
app.use(bodyParser.json());
/**
* Endpoint to process payment authorization.
* The client sends a token and an orderId, which are forwarded to the PSP (Payment Service Provider).
*/
app.post('/api/payment/authorize', async (req, res) => {
const { token, orderId } = req.body;
// Step 1: Validate incoming request data
if (!token || !orderId) {
return res.status(400).json({
success: false,
message: 'Missing token or orderId',
});
}
try {
// Step 2: Forward the payment data to the PSP
const response = await axios.post(
'https://psp.bonum.mn/api/payment/process', // PSP endpoint URL
{
token, // Payment token received from Apple Pay
order_id: orderId, // Merchant invoice/order id
},
{
headers: {
'Content-Type': 'application/json',
'x-merchant-key': 'REPLACE_YOUR_MERCHANT_KEY', // Replace with your actual merchant key
},
}
);
console.log('resp', response.data);
// Step 3: Respond to the client with the PSP's response
res.json({
success: response.data.success, // Indicates if the payment was successful
status_code: response.data.status_code, // PSP's status code for the operation
orderId: response.data.orderId, // The order ID provided in the response
description: response.data.desc, // Additional information about the payment
});
} catch (error) {
console.error('Error processing payment:', error.message);
// Step 4: Handle errors from the PSP or network issues
if (error.response) {
// Errors returned directly by the PSP
return res.status(error.response.status).json({
success: error.response.data.success, // Whether the PSP recognized the payment as successful
status_code: error.response.data.status_code, // PSP-specific error code
orderId: error.response.data.orderId, // Order ID (if available)
description: error.response.data.desc, // PSP-provided error description
});
}
// Step 5: Handle unexpected or generic errors (network, server, etc.)
res.status(500).json({
success: false,
status_code: 500,
orderId, // Original order ID for traceability
description: 'Unexpected error occurred while processing payment.', // Generic error message
});
}
});
// Start the Express server
app.listen(PORT, () => {
console.log("Server is running on http://localhost:" +PORT);
});
Web App > The backend integration
Note: You can use the /api/payment-log/read API for handling any
payment failure cases.
Replace
“REPLACE_YOUR_MERCHANT_KEY"
with your merchant key.
This API call should be executed on your backend.
func checkPaymentByOrderId(orderId: String) {
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer = AFJSONResponseSerializer()
manager.responseSerializer.acceptableContentTypes = manager.responseSerializer.acceptableContentTypes?.union(["application/json"])
//TODO: set Merchant Key here. Bonum will provide Merchant Key.
manager.requestSerializer.setValue("SET-YOUR-MERCHANT-KEY-HERE", forHTTPHeaderField: "x-merchant-key")
//Demo URL. Note: This URL charges the bank transaction. Please use small amount when you test
var urlString = "https://testpsp.bonum.mn/api/payment-log/read"
//Production URL. When you push your Build to the production
//var urlString = "https://psp.bonum.mn/api/payment-log/read"
urlString = urlString + "?order_id=" + orderId
// Convert the JSON object to a JSON string
print("API Url: \(urlString)")
manager.get(urlString,
parameters: nil,
headers: nil, progress: { progress in
print("Download progress: \(progress.totalUnitCount)")
}, success: { task, responseObject in
print("Transactions for Order ID: \(orderId)")
print("GET response: \(responseObject ?? "No response")")
}, failure: { task, error in
print("Error on GET request: \(error.localizedDescription)")
})
}
Web App > The backend integration
Note:
"success": false
indicates that the transaction failed for some reason.
Note:
"success": true
indicates that the transaction was processed successfully.
[
{
"merchant_order_id": "9876543",
"amount": 5000,
"success": false,
"createdAt": "2025-01-24T03:22:03.135Z"
},
{
"merchant_order_id": "9876543",
"amount": 5000,
"success": true,
"createdAt": "2025-01-24T03:22:03.135Z"
}
]
4. IOS App Integration
4.1 Apple Payment Certificate Creation
APPLE PAY > PSP > Payment Certificate
1. Go to Apple Developer Account
2. Log in with your developer account
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
Note: Your Merchant ID has been sent to you via email from Bonum PSP. Please disregard merchant.portal.mn on the screen shot.
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
Merchant ID: merchant.portal.mn
APPLE PAY > PSP > Payment Certificate
APPLE PAY > PSP > Payment Certificate
Note: Select "No" for the question.
APPLE PAY > PSP > Payment Certificate
Upload attached "SigningCertRequest.csr"
Note: The .csr file has been sent to you via email from Bonum
PSP.
APPLE PAY > PSP > Payment Certificate
Download Payment Certificate file and send Payment Certificate file to us.
4.2 In-App Implementation
APPLE PAY > PSP > In App
1. Before proceeding, ensure that you have completed:
- Xcode Project
- Merchant ID (The Merchant ID should have been provided by Bonum PSP, and you needed to create your Merchant ID according to the provided instructions.)
- Your Apple Payment Certificate (This certificate should have been successfully installed on the Bonum PSP server.)
- Merchant Key (The Merchant Key should have been provided by Bonum PSP)
XCODE > Select Target > Signing & Capabilities
XCODE > Select Target > Signing & Capabilities
Search as “Apple Pay”
XCODE > Select Target > Signing & Capabilities
After Apple Pay is enabled, it appears as shown below.
Your Merchant ID should be displayed in place of
'merchant.your.merchant.id'
XCODE > Select Target > Signing & Capabilities
Your Merchant ID should be displayed in place of 'merchant.your.merchant.id' .
XCODE > Select Target > ApplePayViewController
Note: Please read the instruction and replace //TODO: highlighted code with your own code.
The important items to replace in the code are:
1. Replace “merchant.your.merchant.id” with your Merchant ID.
2. Replace “SET-YOUR-MERCHANT-KEY-HERE” with your Merchant Key.
3. Use the order ID (invoice, SKU, etc.) instead of the generateRandomOrderString function. This will allow any payment item to be uniquely identified. We will use it for transaction verification.
ApplePayViewController.swift uses PassKit and “AFNetworking”.
- PassKit is a framework provided by Apple that allows developers to integrate Apple Pay
- AFNetworking is an open-source networking library used for sending payment payload data to the Bonum PSP server.
- Your view controller should implement the PKPaymentAuthorizationControllerDelegate methods.
XCODE > ApplePayViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
//TODO: Implement PKPaymentButton with its style here.
var button: UIButton?
//Checks whether payment can be made or not.
if PKPaymentAuthorizationController.canMakePayments() {
button = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .black)
button?.addTarget(self, action: #selector(startApplePay), for: .touchUpInside)
} else if PKPaymentAuthorizationController.canMakePayments(usingNetworks: [.masterCard, .visa, .amex]) {
button = PKPaymentButton(paymentButtonType: .setUp, paymentButtonStyle: .black)
button?.addTarget(self, action: #selector(setupPressed(_:)), for: .touchUpInside)
}
if let button = button {
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
XCODE > ApplePayViewController.swift
Note: Set the amount and label on the PKPaymentSummaryItem. Selling a multiple items differs from selling single item. See examples in the code.
@objc func startApplePay() {
guard PKPaymentAuthorizationController.canMakePayments() else {
print("Apple Pay is not available on this device.")
return
}
guard PKPaymentAuthorizationController.canMakePayments(usingNetworks: [.masterCard, .visa, .amex]) else {
print("Apple Pay is not set up with a supported card.")
return
}
let paymentRequest = PKPaymentRequest()
paymentRequest.merchantIdentifier = "merchant.your.merchant.id" // TODO: Set Your Merchant ID here
paymentRequest.supportedNetworks = [.masterCard, .visa, .amex]
paymentRequest.merchantCapabilities = [.threeDSecure]
paymentRequest.countryCode = "MN" //Country Code of Mongolia
paymentRequest.currencyCode = "MNT" //Currency code of tugrik
//TODO: One-time purchase for multiple items. Replace the label and amount here.
let item1 = PKPaymentSummaryItem(label: "Apple", amount: NSDecimalNumber(string: "5.1"))
let item2 = PKPaymentSummaryItem(label: "Banana", amount: NSDecimalNumber(string: "4.12"))
//TODO: This item represents the total of all items. Its amount should be the sum of all individual items. For example: 5.1 + 4.12 = 9.22.
let total = PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(string: "9.22"))
paymentRequest.paymentSummaryItems = [item1, item2, total]
// TODO: One-time purchase for a single item. Replace the label and price here.
// let total = PKPaymentSummaryItem(label: "Kiwi", amount: NSDecimalNumber(string: "10"))
// paymentRequest.paymentSummaryItems = [total]
let paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
paymentController.delegate = self
paymentController.present { success in
if success {
print("Payment sheet presented successfully.")
} else {
print("Failed to present payment sheet.")
}
}
}
XCODE > ApplePayViewController.swift
Note:
processPaymentData function will process the
transaction.
convertPKPaymentToJSON function will create Payload JSON
object with order_id for payment process.
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler: @escaping (PKPaymentAuthorizationResult) -> Void) {
print("Payment authorized: \(payment.token)")
print("Payment Network: \(String(describing: payment.token.paymentMethod.network))")
print("Payment Transaction Identifier: \(payment.token.transactionIdentifier)")
processPaymentData(payment) { result in
handler(result)
}
}
func convertPKPaymentToJSON(payment: PKPayment, orderId: String) -> Any? {
// Extract the payment token details
let paymentToken = payment.token
let paymentData = convertToJson(data: paymentToken.paymentData)
let transactionIdentifier = paymentToken.transactionIdentifier
let paymentMethod = paymentToken.paymentMethod
// Extract billing and shipping contacts
let billingContact = payment.billingContact
let shippingContact = payment.shippingContact
// Construct the JSON object
var jsonObject: [String: Any] = [:]
jsonObject["token"] = [
"paymentData": paymentData,
"transactionIdentifier": transactionIdentifier,
"paymentMethod": [
"displayName": paymentMethod.displayName ?? "",
"network": paymentMethod.network?.rawValue ?? "",
"type": paymentMethod.type == .debit ? "debit" :
paymentMethod.type == .credit ? "credit" : "unknown"
],
]
//If unnecessary, don't use
if let billing = billingContact {
jsonObject["billingContact"] = [
"givenName": billing.name?.givenName ?? "",
"familyName": billing.name?.familyName ?? "",
"emailAddress": billing.emailAddress ?? "",
"phoneNumber": billing.phoneNumber?.stringValue ?? ""
]
}
//If unnecessary, don't use
if let shipping = shippingContact {
jsonObject["shippingContact"] = [
"givenName": shipping.name?.givenName ?? "",
"familyName": shipping.name?.familyName ?? "",
"emailAddress": shipping.emailAddress ?? "",
"phoneNumber": shipping.phoneNumber?.stringValue ?? ""
]
}
//Your ORDER ID should be set here
jsonObject["order_id"] = orderId
return jsonObject
}
XCODE > ApplePayViewController.swift
Note:
processPaymentData function will process the
transaction.
Recommendation: Server-to-Server /api/payment/process API
call.
This API call should be executed on your backend.
func processPaymentData(_ payment: PKPayment, completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer = AFJSONResponseSerializer()
manager.responseSerializer.acceptableContentTypes = manager.responseSerializer.acceptableContentTypes?.union(["application/json"])
//TODO: set Merchant Key here. Bonum will provide Merchant Key.
manager.requestSerializer.setValue("SET-YOUR-MERCHANT-KEY-HERE", forHTTPHeaderField: "x-merchant-key")
//TODO: Set Payment process API url here
//Demo URL. Note: This URL charges the bank transaction. Please use small amount when you test
let urlString = "https://testpsp.bonum.mn/api/payment/process"
//Production URL. When you push your Build to the production
//let urlString = "https://psp.bonum.mn/api/payment/process"
//TODO: set your order/invoice/sku/ id here.
let orderId = generateRandomOrderString(length: 20)
orderIdLbl.text = orderId
let jsonPayload = convertPKPaymentToJSON(payment: payment, orderId: orderId)
// Convert the JSON object to a JSON string
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonPayload ?? "", options: .prettyPrinted) {
let jsonString = String(data: jsonData, encoding: .utf8)
print("Payload data: \(jsonString ?? "No response")")
}
manager.post(urlString, parameters: jsonPayload, headers: nil, progress: { progress in
print("Download progress: \(progress.totalUnitCount)")
}, success: { task, responseObject in
print("POST response: \(responseObject ?? "No response")")
// Safely cast responseObject to [String: Any]
if let responseDict = responseObject as? [String: Any] {
// Access the boolean field
if let success = responseDict["success"] as? Bool {
print("Success: \(success)") // Output: Success: true/false
if success {
let successResult = PKPaymentAuthorizationResult(status: .success, errors: nil)
completion(successResult)
} else {
let failureResult = PKPaymentAuthorizationResult(status: .failure, errors: nil)
completion(failureResult)
}
} else {
print("Key 'success' is missing or not a boolean.")
let failureResult = PKPaymentAuthorizationResult(status: .failure, errors: nil)
completion(failureResult)
}
} else {
print("Failed to parse response as dictionary.")
let failureResult = PKPaymentAuthorizationResult(status: .failure, errors: nil)
completion(failureResult)
}
}, failure: { task, error in
print("Error on GET request: \(error.localizedDescription)")
let failureResult = PKPaymentAuthorizationResult(status: .failure, errors: nil)
completion(failureResult)
})
}
XCODE > ApplePayViewController.swift
{
"token":{
"paymentMethod":{
"type":"debit",
"network":"MasterCard",
"displayName":"MasterCard 1588"
},
"transactionIdentifier":"4be88bbec5d887d696b536b87d9a28a25d5ca5e6cbbac6e06bb3c14b0967f16f",
"paymentData":{
"data":"lAGF6hAH+z\/vZz1UG",
"signature":"BkAiA+zmtT6wYi6JxyrpjEqYSYRw82kMhjxXSZIvyd1G9zNgAAAAAAAA==",
"header":{
"publicKeyHash":"EalQwLcwQ0Mqh9R1qWtAwsO4Eht+6T2w9pMNypx2sP8=",
"ephemeralPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzy8Acl6MTWG7luHdWeA\/sI+7mXMczg698cdOB9lYJBVVg2WYNgP4Ila4+vLfqvYQmBurR7wK1UQ2W8Ma2kNUAA==",
"transactionId":"4be88bbec5d887d696b536b87d9a28a25d5ca5e6cbbac6e06bb3c14b0967f16f"
},
"version":"EC_v1"
}
},
"order_id":"1nnuqsc4j6bzpl5hm26z"
}
XCODE > ApplePayViewController.swift
Note: You can use the /api/payment-log/read API for handling any
payment failure cases.
Recommendation: Server-to-Server /api/payment-log/read
API call.
This API call should be executed on your backend.
func checkPaymentByOrderId(orderId: String) {
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer = AFJSONResponseSerializer()
manager.responseSerializer.acceptableContentTypes = manager.responseSerializer.acceptableContentTypes?.union(["application/json"])
//TODO: set Merchant Key here. Bonum will provide Merchant Key.
manager.requestSerializer.setValue("SET-YOUR-MERCHANT-KEY-HERE", forHTTPHeaderField: "x-merchant-key")
//Demo URL. Note: This URL charges the bank transaction. Please use small amount when you test
var urlString = "https://testpsp.bonum.mn/api/payment-log/read"
//Production URL. When you push your Build to the production
//var urlString = "https://psp.bonum.mn/api/payment-log/read"
urlString = urlString + "?order_id=" + orderId
// Convert the JSON object to a JSON string
print("API Url: \(urlString)")
manager.get(urlString,
parameters: nil,
headers: nil, progress: { progress in
print("Download progress: \(progress.totalUnitCount)")
}, success: { task, responseObject in
print("Transactions for Order ID: \(orderId)")
print("GET response: \(responseObject ?? "No response")")
}, failure: { task, error in
print("Error on GET request: \(error.localizedDescription)")
})
}
XCODE > ApplePayViewController.swift
Note:
"success": false
indicates that the transaction failed for some reason.
Note:
"success": true
indicates that the transaction was processed successfully.
[
{
"merchant_order_id": "9876543",
"amount": 5000,
"success": false,
"createdAt": "2025-01-24T03:22:03.135Z"
},
{
"merchant_order_id": "9876543",
"amount": 5000,
"success": true,
"createdAt": "2025-01-24T03:22:03.135Z"
}
]
5. SUPPORT & CONTACT
References:
Apple Pay on Web Demo by Apple - https://applepaydemo.apple.com
Apple Pay Human Interface Guidelines - https://developer.apple.com/design/human-interface-guidelines/apple-pay
Apple Pay Marketing Resources - https://developer.apple.com/apple-pay/marketing
Apple Pay Marketing Guidelines - https://developer.apple.com/apple-pay/marketing
If you need further assistance, please contact our team:
- Email:
info@bonum.mn
- Phone: +976 7200-5000