I

Integrating Alipay into Vapor Framework

Damon胡东东 2026-03-25

Since the official Alipay SDK does not provide a Swift version, you need to implement signing and verification yourself. This article uses Swift version 5.7+ in conjunction with krzyzanowskim/CryptoSwift. Of course, using vapor/jwt-kit is also an option; jwt-kit is actually more convenient (no need to handle key formats manually) and has been verified to work. However, since I required other features from CryptoSwift, I will use CryptoSwift as the example here.

Key Handling

1. Generate Application Public and Private Keys

Download the Alipay Open Platform Key Tool to generate your keys. Use the RSA2 encryption algorithm to generate the Application Public Key and Application Private Key. By default, the tool generates X.509/SPKI and PKCS#8 formats, which need to be converted to PKCS#1.

2. Obtain the Alipay Public Key

2025-08-12T04:01:37.png

In the Alipay Merchant Dashboard, set the "Signature Method" (加签方式). Copy the Application Public Key from the tool into the input box and click confirm to upload. You will then obtain the Alipay Public Key. Download this key as a .txt file. The Application Public Key's job is now done and can be deleted.

3. Processing the Alipay Public Key

Open the downloaded Alipay Public Key .txt file and add the identifiers at the header and footer:

// Add to Header
-----BEGIN PUBLIC KEY-----
// Add to Footer
-----END PUBLIC KEY-----

The modified content should look similar to:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ……
-----END PUBLIC KEY-----

Execute the following command in your terminal to convert it to PKCS#1 format. This will yield the alipayPublicKey_RSA2_pkcs1.txt file for subsequent use:

openssl rsa -pubin -in alipayPublicKey_RSA2.txt -RSAPublicKey_out -out alipayPublicKey_RSA2_pkcs1.txt

4. Application Private Key Processing

Rename the Application Private Key .txt file generated by the tool to private_pkcs8.txt. Open the file and add the following identifiers:

// Add to Header
-----BEGIN PRIVATE KEY-----
// Add to Footer
-----END PRIVATE KEY-----

The modified content should look like:

-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASC……
-----END PRIVATE KEY-----

Execute the following command to convert it to PKCS#1 format, resulting in private_pkcs1.txt:

openssl pkey -in private_pkcs8.txt -out private_pkcs1.txt -traditional

Initiating Payment

Key Points:

  1. The form's gateway.do needs the charset parameter set, otherwise it defaults to GBK.
var html = "<html><body onload='document.forms[0].submit()'>"
html += "<form method='POST' action='https://openapi.alipay.com/gateway.do?charset=utf-8'>"
  1. Construct the POST parameter dictionary. Do not remove sign_type during encryption, and use the PKCS#1 format private key for signing.
  2. The signing function expects the digest or the message. You can use:

String concatenation for signing:

let unsignedString = parameters
            .filter { $0.key != "sign"}
            .sorted(by: { $0.key < $1.key })
            .map { "\($0)=\($1)" }
            .joined(separator: "&")

Signing implementation:

privateKeyRAS.sign(Data(unsignedString.utf8).byteArray.sha256(), variant: .digest_pkcs1v15_SHA256)

OR

privateKeyRAS.sign(Data(unsignedString.utf8).byteArray, variant: .message_pkcs1v15_SHA256)

Payment Verification (Verification of Signature)

Key Points:

  1. Use the PKCS#1 formatted Alipay Public Key generated earlier, NOT the Application Public Key generated by the tool.
  2. In the notification return parameter list, all parameters except sign and sign_type are parameters to be verified. (Note: Asynchronous notifications for "Life Accounts" may require keeping sign_type).
  3. Perform url_decode on the remaining parameters, sort them alphabetically by key, and concatenate them into a string to get the "to-be-signed" string.
  4. Decode the sign parameter from Base64 into a byte array.

String concatenation for verification:

let unsignedString = params
            .filter { $0.key != "sign" && $0.value != "" && $0.key != "sign_type" }
            .sorted(by: { $0.key < $1.key })
            .map { "\($0.key)=\($0.value)" }
            .joined(separator: "&")

Verification implementation:

let publicKeyRAS = try RSA(rawRepresentation: publicKeyData)
let result = try publicKeyRAS.verify(signature: signData.byteArray, for: Data(unsignedString.utf8).byteArray, variant: .message_pkcs1v15_SHA256)
return result
NEXT
Why I Abandoned the Image-Grid Waterfall Layout for My Tech Blog

Comments(0)

Submit comment.