不管是微信支付还是支付宝支付, 3个最棘手的问题是: 1,如何生成签名 2,支付请求如何提交 3, 如何验证签名
下面就围绕这二个问题来讲。
我使用的是XE3.
先看微信支付: 1,微信支付的签名生成
uses IdHashMessageDigest, NatvieXml; //我使用的是NativeXml4.07
function TMicroPayParamBuilder.GetSign: string; var Digest: TIdHashMessageDigest5; utf8: UTF8String; begin utf8 := '';
Assert(FAppId <> '', '公众账号ID 不能为空!'); utf8 := utf8 + 'appid=' + FAppId;
if FAttach <> '' then utf8 := utf8 + '&attach=' + FAttach;
Assert(FAuthCode <> '', '授权码 不能为空!'); utf8 := utf8 + '&auth_code=' + FAuthCode;
Assert(FBody <> '', '商品描述 不能为空!'); utf8 := utf8 + '&body=' + FBody;
if FDetail <> '' then utf8 := utf8 + '&detail=' + BuildDetail();
Assert(FMchId <> '', '商户号 不能为空!'); utf8 := utf8 + '&mch_id=' + FMchId;
utf8 := utf8 + '&nonce_str=' + FNonceStr;
Assert(FOutTradeNo <> '', '商户订单号 不能为空!'); utf8 := utf8 + '&out_trade_no=' + FOutTradeNo;
Assert(FSpbillCreateIp <> '', '终端IP 不能为空!'); utf8 := utf8 + '&spbill_create_ip=' + FSpbillCreateIp;
Assert(FTotalFee > 0, '订单金额 必须大于0!'); utf8 := utf8 + '&total_fee=' + IntToStr(FTotalFee);
Assert(Fkey <> '', '密钥 不能为空!'); utf8 := utf8 + '&key=' + Fkey;
//计算签名 try Digest:= TIdHashMessageDigest5.Create; Result := Digest.HashBytesAsHex(BytesOf(utf8)); finally Digest.Free; end; end;
//生成Http Post请求的数据 function TMicroPayParamBuilder.BuildParam: string; var xml : TNativeXml; Node: TXmlNode; begin xml := TNativeXml.CreateEx(nil, False, False, True, 'xml'); try Node:= xml.NodeNewTextType('appid', FAppId , xeElement); xml.Root.NodeAdd(Node);
Node:= xml.NodeNewTextType('mch_id', FMchId , xeElement); xml.Root.NodeAdd(Node);
Node:= xml.NodeNewTextType('nonce_str', FNonceStr , xeElement); xml.Root.NodeAdd(Node);
Node:= xml.NodeNewTextType('sign', Sign , xeElement); xml.Root.NodeAdd(Node);
Node:= xml.NodeNewTextType('body', FBody , xeElement); xml.Root.NodeAdd(Node);
Node:= xml.NodeNewTextType('out_trade_no', FOutTradeNo , xeElement); xml.Root.NodeAdd(Node);
Node:= xml.NodeNewIntType('total_fee', FTotalFee , xeElement); xml.Root.NodeAdd(Node);
Node:= xml.NodeNewTextType('spbill_create_ip', FSpbillCreateIp , xeElement); xml.Root.NodeAdd(Node);
Node:= xml.NodeNewTextType('auth_code', FAuthCode , xeElement); xml.Root.NodeAdd(Node);
if FAttach <> '' then begin Node:= xml.NodeNewTextType('attach', FAttach , xeElement); xml.Root.NodeAdd(Node); end;
if FDetail <> '' then begin Node:= xml.NodeNewTextType('detail', BuildDetail() , xeElement); xml.Root.NodeAdd(Node); end;
Result := xml.WriteToString;
finally xml.Free; end; end;
2, 微信支付请求如何提交 class function TTencentSSLHttpPost.Post(URL: string; Builder: TBaseParamBuilder): string; var ssl:TIdSSLIOHandlerSocketOpenSSL; http: TIdHttp; inStrm, outStrm: TStringStream; begin http:= TIdHttp.Create(nil); ssl:=TIdSSLIOHandlerSocketOpenSSL.Create(nil); ssl.SSLOptions.Method := sslvSSLv23; http.IOHandler := ssl; inStrm:= TStringStream.Create(Builder.BuildParam, TEncoding.UTF8); outStrm:= TStringStream.Create('', TEncoding.UTF8); try try http.Post(URL, inStrm, outStrm); Result := outStrm.DataString;
except Result := ''; end; finally ssl.Free; http.Free; outStrm.Free; inStrm.Free; end; end;
const URL_MicroPay = 'https://api.mch.weixin.qq.com/pay/micropay';
class function TMicroPayHttpUtils.PostRequest( Builder: TMicroPayParamBuilder): string; begin Result := TTencentSSLHttpPost.Post(URL_MicroPay, Builder); end;
微信如何验证签名:
TStringsHelper = class helper for TStrings function Join(const Splitter: string): string; end;
{ TMyStrings}
function TStringsHelper.Join(const Splitter: string): string; var I : Integer; begin Result := ''; for I := 0 to Self.Count - 1 do Result := Result + Splitter + Self.Names[I] + '=' + Self.ValueFromIndex[I];
if Result <> '' then System.Delete(result, 1, Length(Splitter)); end;
function MyStringListSort(List: TStringList; Index1, Index2: Integer): Integer; begin Result := CompareStr(List.Names[Index1], List.Names[Index2]); end;
function VerifyResponseSign(xml: TNativeXml; Key: string): Boolean; overload; var sign: string; I : Integer; List : TStringList; utf8: UTF8String; Digest: TIdHashMessageDigest5; begin Result := False;
if xml.Root.FindNode('sign') = nil then Exit; sign := xml.Root.FindNode('sign').ValueUnicode;
List := TStringList.Create; try for I := 0 to xml.Root.NodeCount - 1 do begin if (xml.Root.Nodes[I].NameUnicode <> 'sign') and (xml.Root.Nodes[I].NameUnicode <> 'WhiteSpace') and (xml.Root.Nodes[I].ValueUnicode <> '') then List.Add(xml.Root.Nodes[I].NameUnicode + '=' + xml.Root.Nodes[I].ValueUnicode); end; List.CustomSort(MyStringListSort);
utf8 := List.Join('&') + '&key=' + key;
try Digest:= TIdHashMessageDigest5.Create; Result := SameText(Digest.HashBytesAsHex(BytesOf(utf8)), sign); finally Digest.Free; end; finally List.Free; end; end;
function VerifyResponseSign(xml: TNativeXml): Boolean; overload; begin Result := VerifyResponseSign(xml, GetKey()); end;
function VerifyResponseSign(Response: string; Key: string): Boolean; overload; var xml: TNativeXml; begin Result := False; xml := TNativeXml.Create(nil); try try xml.ReadFromString(Response); except Exit; end;
Result := VerifyResponseSign(xml, Key); finally xml.Free; end; end;
那么如何发送一个刷卡支付请求呢? procedure TForm4.btn4Click(Sender: TObject); var Param: TMicroPayParamBuilder; Response: string; begin Param:= TMicroPayParamBuilder.Create; try if edBody.Text <> '' then Param.Body := edBody.Text; Param.OutTradeNo:= edTradeNo.Text; Param.SpbillCreateIp:=idpwtch1.LocalIP; param.AuthCode:= edAuthCode.Text; Param.Attach := edAttach.Text; Param.TotalFee := 10;
param.AppId := edAppId.Text; Param.MchId := edMch_id.Text; Param.Key := edKey.Text;
mmo1.Lines.Append(Param.BuildParam); mmo1.Lines.Append('-------------------------------------------');
Response := TMicroPayHttpUtils.PostRequest(Param);
if Response = '' then begin ShowMessage('请求出错! 可能是网络不通!'); Exit; end;
mmo1.Lines.Append(Response);
if not VerifyResponseSign(Response, GetKey()) then showmessage('签名验证不通过!');
finally Param.Free; end; end;
|
请发表评论