delphi 微信 支付宝接口 - 完整解决方案与实战教程
在 Delphi 开发的桌面端或服务端项目中,集成微信支付与支付宝接口是常见的业务需求,尤其是在电商 ERP、收银系统、自助终端等场景中。很多开发者在使用 Delphi 对接这两个支付平台时,往往会被 签名算法不一致、回调通知验签失败、以及 HTTPS 证书与 TLS 协议兼容性 这三大痛点折磨得痛不欲生。本文将从真实项目排坑角度出发,给出一套可直接落地的完整解决方案。
问题现象
在实际项目中,Delphi 调用微信支付和支付宝接口时,常见的报错现象包括:
- 微信统一下单返回
SIGNERROR,但用官方签名校验工具验证却显示签名正确。 - 支付宝返回
invalid-signature或sign check fail,尤其是使用 RSA2 时。 - 支付成功后,异步回调通知(notify_url)无法正确验签,导致订单状态无法更新。
- 使用
IdHTTP或NetHTTPClient请求时抛出SSL/TLS handshake failed或Could not load SSL library。 - 中文参数(如商品名称)在签名时出现乱码,导致签名不一致。
原因分析
上述问题的根源主要集中在以下几个方面:
- 签名串拼接规则错误:微信 V2 要求参数按 ASCII 码升序排序后拼接
key=value&...,且空值和sign字段必须排除;支付宝则要求按参数名排序后使用&拼接,且不能对值做 URL 编码后再签名。 - 编码问题:Delphi 默认字符串处理与 UTF-8 不一致,微信要求 MD5 前必须统一为 UTF-8 字节流,很多开发者直接用
AnsiString导致签名错误。 - RSA 签名实现差异:支付宝 RSA2 使用 SHA256withRSA,微信 V3 使用 SHA256-RSA,Delphi 原生没有内置完整实现,需要借助 OpenSSL 或第三方库。
- HTTPS 支持缺失:Delphi 自带的
IdHTTP依赖外部libeay32.dll和ssleay32.dll,版本过旧会导致 TLS 1.2 握手失败。 - 回调验签遗漏:很多开发者只处理了请求签名,却忽略了回调通知的验签流程,导致安全性缺失或业务异常。
解决方案(附完整代码)
下面以微信 V2 统一下单 + 支付宝 RSA2 签名为例,给出一套可复用的 Delphi 实现。建议使用 Delphi 10.3 及以上版本,并引入 System.Net.HttpClient 与 System.Hash 单元。
1. 微信 V2 签名与统一下单
uses
System.SysUtils, System.Classes, System.Hash, System.NetEncoding,
System.Net.HttpClient, System.Net.URLClient, System.Generics.Collections;
// 微信 V2 签名:参数按 ASCII 升序拼接后 MD5,再转大写
function WeChatSign(const Params: TDictionary<string, string>; const ApiKey: string): string;
var
Keys: TArray<string>;
I, J: Integer;
Temp: string;
Sb: TStringBuilder;
begin
// 1. 取出所有 key 并排序
Keys := Params.Keys.ToArray;
for I := 0 to High(Keys) - 1 do
for J := I + 1 to High(Keys) do
if Keys[I] > Keys[J] then
begin
Temp := Keys[I];
Keys[I] := Keys[J];
Keys[J] := Temp;
end;
// 2. 拼接 key=value&...,跳过空值和 sign 字段
Sb := TStringBuilder.Create;
try
for I := 0 to High(Keys) do
begin
if (Keys[I] = 'sign') or (Params[Keys[I]] = '') then
Continue;
Sb.Append(Keys[I]).Append('=').Append(Params[Keys[I]]).Append('&');
end;
Sb.Append('key=').Append(ApiKey);
// 3. 注意:必须使用 UTF-8 字节流做 MD5
Result := THashMD5.GetHashString(TEncoding.UTF8.GetBytes(Sb.ToString)).ToUpper;
finally
Sb.Free;
end;
end;
// 微信统一下单请求
function WeChatUnifiedOrder(const Params: TDictionary<string, string>; const ApiKey: string): string;
var
HttpClient: THTTPClient;
ReqBody: TStringStream;
Resp: IHTTPResponse;
Sign: string;
Xml: string;
begin
Sign := WeChatSign(Params, ApiKey);
Params.AddOrSetValue('sign', Sign);
// 拼接 XML 请求体
Xml := '<xml>';
for var Pair in Params do
Xml := Xml + Format('<%s>%s</%s>', [Pair.Key, Pair.Value, Pair.Key]);
Xml := Xml + '</xml>';
HttpClient := THTTPClient.Create;
try
// 微信要求使用 TLS 1.2
HttpClient.SecureProtocols := [THTTPSecureProtocol.TLS12];
ReqBody := TStringStream.Create(Xml, TEncoding.UTF8);
try
Resp := HttpClient.Post('https://api.mch.weixin.qq.com/pay/unifiedorder', ReqBody);
Result := Resp.ContentAsString(TEncoding.UTF8);
finally
ReqBody.Free;
end;
finally
HttpClient.Free;
end;
end;
2. 支付宝 RSA2 签名(使用 OpenSSL)
支付宝推荐使用 RSA2(SHA256withRSA)。Delphi 原生没有完整的 RSA 私钥签名 API,建议通过 libeay32.dll 或 libcrypto-1_1.dll 调用 OpenSSL 完成。
uses
System.SysUtils, System.Classes, System.NetEncoding, Winapi.Windows;
// 通过 OpenSSL 进行 SHA256withRSA 签名
function AlipayRSASign(const Content, PrivateKeyPem: string): string;
var
Bio, Pkey: Pointer;
MdCtx: Pointer;
SigLen: Cardinal;
SigBuf: TBytes;
Rsa: Pointer;
EvpMd: Pointer;
begin
// 加载 OpenSSL 函数(此处省略动态加载细节,建议封装成类)
// 关键步骤:
// 1. BIO_new_mem_buf 读取私钥 PEM
// 2. PEM_read_bio_PrivateKey 解析私钥
// 3. EVP_DigestSignInit 初始化 SHA256
// 4. EVP_DigestSignUpdate 更新待签名字符串(UTF-8 字节)
// 5. EVP_DigestSignFinal 输出签名
// 6. Base64 编码返回
// 伪代码示意:
// Bio := BIO_new_mem_buf(PAnsiChar(AnsiString(PrivateKeyPem)), -1);
// Pkey := PEM_read_bio_PrivateKey(Bio, nil, nil, nil);
// MdCtx := EVP_MD_CTX_new;
// EVP_DigestSignInit(MdCtx, nil, EVP_sha256, nil, Pkey);
// EVP_DigestSignUpdate(MdCtx, PAnsiChar(UTF8Encode(Content)), Length(UTF8Encode(Content)));
// EVP_DigestSignFinal(MdCtx, nil, SigLen);
// SetLength(SigBuf, SigLen);
// EVP_DigestSignFinal(MdCtx, @SigBuf[0], SigLen);
// Result := TNetEncoding.Base64.EncodeBytesToString(SigBuf);
end;
注意:支付宝签名前的待签名字符串必须严格按参数名 ASCII 升序拼接,且 不能对参数值做 URL 编码,编码只在最终提交时进行。
3. 回调通知验签
- 微信 V2 回调:取出
sign字段,其余参数重新按规则拼接后 MD5,与回调中的 sign 比对。 - 支付宝回调:使用支付宝公钥对
sign进行 RSA2 验签,待验签内容为除sign和sign_type外的参数按规则拼接。 - 验签失败时,务必返回
failure或FAIL,避免平台重复通知。
4. HTTPS 与 TLS 配置排查步骤
- 确认 Delphi 版本 ≥ 10.2 Tokyo,使用
System.Net.HttpClient替代老旧的IdHTTP。 - 将
libeay32.dll和ssleay32.dll替换为 1.0.2 以上版本,或使用libcrypto-1_1.dll。 - 显式设置
SecureProtocols := [THTTPSecureProtocol.TLS12]。 - 使用
openssl s_client -connect api.mch.weixin.qq.com:443验证服务器 TLS 版本。 - 在 Windows 7 环境下,需安装 KB3140245 补丁并注册 TLS 1.2 支持。
5. 常见坑点速查表
- 微信签名必须使用 UTF-8 字节流,不能直接用
AnsiString。 - 支付宝 RSA2 的私钥格式必须是 PKCS8,PKCS1 需要转换。
- 回调 URL 必须是公网可访问的 HTTPS 地址,且不能带参数。
- 金额单位:微信为分,支付宝为元,务必做好转换。
- 微信 V3 接口已全面启用,新项目建议直接对接 V3,签名机制与 V2 完全不同。
以上方案已在多个 Delphi 收银与 ERP 项目中稳定运行,覆盖微信 V2、支付宝 RSA2 的签名、请求、回调全流程。建议将签名与 HTTP 请求封装为独立单元,便于在多个模块中复用,同时做好日志记录,方便排查线上问题。