Created
September 18, 2018 09:14
-
-
Save kayoslab/0998523f0d2e6ceecb525d9e3bdb9efe to your computer and use it in GitHub Desktop.
Swift port of the Legacy Alipay Order, which is usually only available in Objective-C. I rely on the RSA implementation of SwiftyRSA here, so that I don't have to work with their own (obj-c) implementations.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import SwiftyRSA | |
/// Swift port of the always included _Order_ class from Alipay, because we suffered from the terrible code. | |
/// The code comments went through google translate to give us some hints on what is happening under the hood. | |
/// This struct is used to hold the data for an order within the alipay framework and to get the url parameter | |
/// containing the order object including a RSA signature. | |
private struct LegacyAlipayOrder { | |
/********************************** Pay four elements **********************************/ | |
/// When the merchant signs an agreement with Alipay, Alipay assigns a unique | |
/// identification number to the merchant (16-digit pure number starting with 2088). | |
private var partner: String? | |
/// The Alipay unique user number corresponding to the Alipay account (16-bit pure | |
/// number starting with 2088), the order payment amount will be entered into the | |
/// account, and a partner can correspond to multiple seller_ids. | |
private var sellerID: String? | |
/// The unique order number for the merchant's website item. | |
private var outTradeNO: String? | |
/// The total amount of funds for this order, in RMB (Yuan). | |
/// The value range is [0.01, 100000000.00], which is accurate to two decimal places. | |
private var totalFee: String? | |
/********************************** Commodity related **********************************/ | |
/// Product title/transaction title/order title/order keyword, etc. | |
private var subject: String? | |
/// A specific description of a transaction. If it is a variety of products, | |
/// please add the product description string to the body. | |
private var body: String? | |
/****************************** Other mandatory parameters *****************************/ | |
/// The interface name is fixed to mobile.securitypay.pay. | |
private var service: String? | |
/// The encoding format used by the merchant website is fixed to utf-8. | |
private var inputCharset: String? | |
/// The Alipay server proactively informs the http path of the page specified in the merchant's website. | |
private var notifyURL: String? | |
private var returnURL: String? | |
private var currency: String? | |
private var forexbiz: String? | |
/********************************* Optional parameters *********************************/ | |
/// Payment type, 1: Purchase of goods. (the default value is not passed) | |
private var paymentType: String? | |
/// Specifically distinguish the type of goods in the local transaction, 1: physical | |
/// transaction; (default value in the case of no transmission), 0: virtual transaction; | |
/// (restriction of rules such as credit card is not allowed). | |
private var goodsType: String? | |
/// Whether to initiate real-name verification when paying, | |
/// F: Do not initiate real-name verification; (default value in case of no transmission), | |
/// T: Initiate real-name verification; (Business business requires buyer real-name authentication) | |
private var rnCheck: String? | |
/// Identifies the client. | |
private var appID: String? | |
/// Identifies the client source. The content of the parameter value is as follows: | |
/// appenv=“system=client platform name^version=business system version” | |
private var appenv: String? | |
/// Set the timeout for unpaid transactions. Once the timeout expires, | |
/// the transaction will be automatically closed. When the user enters the payment password | |
/// and clicks to confirm the payment (that is, after creating the Alipay transaction), | |
/// the timing starts. Value range: 1m to 15d, or use absolute time (example format: 2014-06-13 16:00:00). | |
/// M-minute, h-hour, d-day, 1c-day (1c-day case, no matter when the transaction is created, it is closed at 0). | |
/// The value of this parameter does not accept decimal points, such as 1.5h, which can be converted to 90m. | |
private var itBPay: String? | |
/// Product address | |
private var showURL: String? | |
/// Business extension parameters, Alipay specific business needs to add this field, json format. | |
/// When the merchant accesses, it is determined by negotiation with Alipay. | |
private var outContext: Dictionary<String, Any>? | |
private var description: String { | |
var description: String = "" | |
/** Pay elements **/ | |
if let partner = partner { | |
description = description.appendingFormat("partner=\"%@\"", partner) | |
} | |
if let sellerID = sellerID { | |
description = description.appendingFormat("&seller_id=\"%@\"", sellerID) | |
} | |
if let outTradeNO = outTradeNO { | |
description = description.appendingFormat("&out_trade_no=\"%@\"", outTradeNO) | |
} | |
if let totalFee = totalFee { | |
description = description.appendingFormat("&total_fee=\"%@\"", totalFee) | |
} | |
/** Commodity related **/ | |
if let subject = subject { | |
description = description.appendingFormat("&subject=\"%@\"", subject) | |
} | |
if let body = body { | |
description = description.appendingFormat("&body=\"%@\"", body) | |
} | |
/** Other mandatory parameters **/ | |
if let service = service { | |
description = description.appendingFormat("&service=\"%@\"", service) | |
} | |
if let inputCharset = inputCharset { | |
description = description.appendingFormat("&_input_charset=\"%@\"", inputCharset) | |
} | |
if let notifyURL = notifyURL { | |
description = description.appendingFormat("¬ify_url=\"%@\"", notifyURL) | |
} | |
if let returnURL = returnURL { | |
description = description.appendingFormat("&return_url=\"%@\"", returnURL) | |
} | |
if let currency = currency { | |
description = description.appendingFormat("¤cy=\"%@\"", currency) | |
} | |
if let forexbiz = forexbiz { | |
description = description.appendingFormat("&forex_biz=\"%@\"", forexbiz) | |
} | |
/** Optional parameters **/ | |
if let paymentType = paymentType { | |
description = description.appendingFormat("&payment_type=\"%@\"", paymentType) | |
} | |
if let itBPay = itBPay { | |
description = description.appendingFormat("&it_b_pay=\"%@\"", itBPay) | |
} | |
if let showURL = showURL { | |
description = description.appendingFormat("&show_url=\"%@\"", showURL) | |
} | |
if let appID = appID { | |
description = description.appendingFormat("&app_id=\"%@\"", appID) | |
} | |
if let outContext = outContext { | |
for (key, value) in outContext.enumerated() { | |
description = description.appendingFormat("&%@=\"%@\"", key, value as? CVarArg ?? "") | |
} | |
} | |
return description | |
} | |
func description(with key: PrivateKey) throws -> String? { | |
guard let signed = try signedString(with: key) else { return nil } | |
return description + "&sign=\"" + signed + "\"&sign_type=\"RSA\"" | |
} | |
private func signedString(with key: PrivateKey) throws -> String? { | |
/// Alipay allows all characters, but "!*'();:@&=+$,/?%#[]". | |
let characterset = CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]").inverted | |
/// Contains the UTF8 encoded Message object with the order's description. | |
let message = try ClearMessage(string: description, using: .utf8) | |
/// The message's signature using SHA1. | |
let signature = try message.signed(with: key, digestType: Signature.DigestType.sha1) | |
/// Percent encoded signature as base64 string. | |
let wrappedSignedString = signature.base64String.addingPercentEncoding(withAllowedCharacters: characterset) | |
// Let's be sure everything worked out as expected. | |
guard let signedString = wrappedSignedString, !signedString.isEmpty else { return nil } | |
return signedString | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment