1. 程式人生 > >DELPHI支付寶支付程式碼

DELPHI支付寶支付程式碼

  真實業務場景的考慮

按照支付寶或者微信支付的開發手冊的說法,一個標準的客戶端接入支付業務模型應該是這樣的,我忽略時序圖,只用文字描述:

  1. 使用者登入客戶端,選擇商品,然後點選客戶端支付。
  2. 客戶端收集商品資訊,然後呼叫自己業務平臺的預付款介面。
  3. 業務平臺根據客戶端提交的商品資訊,生成自己的訂單號等內容,並按照第三方支付的要求對訂單資訊進行拼裝組合,並編碼和簽名。
  4. 業務平臺返回簽名後的訂單資訊給客戶端。
  5. 客戶端根據這個簽名信息,撥出客戶端的第三方支付系統,完成支付。

1)準備工作:

支付到指定的商家(一般就是軟體開發商自己啦),商家需要事先在支付寶的開放平臺申請開通【支付寶無線快捷支付】,具體請百度。
開通【無線快捷支付】後,支付寶應該返回給商家如下幾個重要引數:
 PARTNER(商戶ID): 一個16為的字串,應該都是數字。
 SELLER(商戶名):多數是商戶的郵件地址。
 RSA_PRIVATE(商戶私鑰 ): 一個848位的字串,採用pkcs8編碼過。
 RSA_PRIVATE(商戶私鑰 ): 一個848位的字串。
 RSA_PUBLIC(商戶公鑰): 一個218位的經過pkcs8編碼的字串。這個公鑰並沒有用到,是支付寶後臺用的。
以上幾個引數,都是支付寶再稽核通過後,通過郵件傳送給商家的,其中RSA的兩個公私鑰,需要商戶自己根據手冊自行生成並上傳和儲存,具體這一步請務必參考支付寶開放平臺的說明。

2)DELPHI支付寶程式碼:

// cxg 2016-11-21
// edit by xe10.1 update1

unit zhifubao;

interface

uses
SysUtils, windows, classes, dialogs, IdSSLOpenSSL, IdSSLOpenSSLHeaders, IdCoderMIME, IdGlobal, IdHTTP;

type
TZhiFuBao = class
// 支付寶支付的簽名生成 支付寶sdk要求,需要對訂單資訊進行rsa簽名
class function GenarateRSASign(PrivateKey, Content: AnsiString): AnsiString;
// 支付寶如何提交請求
class function SSLHttpPost(URL, OrderInfo: string): string;
// 支付寶支付如何驗證簽名
class function VerifyRSASign(const PublicKey, Content, Sign: AnsiString): Boolean;
// 訂單格式化
class function GetOrderInfo(PARTNER, SELLER, subject, body, price: string): string;
// 生成訂單號 保證不重複即可,隨便寫
class function GetTrackNo: string;
end;

implementation

{ TZhiFuBao }

class function TZhiFuBao.GenarateRSASign(PrivateKey, Content: AnsiString): AnsiString;
var
buffer: PAnsiChar;
rsa_out, md: array[0..1023] of AnsiChar;
r: PRSA;
rsa_out_len: Cardinal;
key: PBIO;
hIdCrypto: HMODULE;
hash: array[0..SHA_DIGEST_LENGTH - 1] of AnsiChar;
type
Trsa_sign = function(_type: LongInt; const m: PAnsiChar; m_length: LongWord; sigret: PAnsiChar; var siglen: Cardinal; const rsa: PRSA): LongInt; cdecl;

Tsha1 = function(d: PAnsiChar; n: Cardinal; md: PAnsiChar): PAnsiChar; cdecl;

function LoadFunctionCLib(const FceName: string; const ACritical: Boolean = True): Pointer;
begin
Result := GetProcAddress(hIdCrypto, PChar(FceName));
end;

var
rsa_sign: Trsa_sign;
sha1: Tsha1;
bytes: TIdBytes;
begin
LoadOpenSSLLibrary;
hIdCrypto := LoadLibrary('libeay32.dll');
Assert(hIdCrypto <> 0, '無法載入libeay32.dll');
try
key := BIO_new_mem_buf(@PrivateKey[1], Length(PrivateKey));
r := PEM_read_bio_RSAPrivateKey(key, nil, nil, nil);
if r = nil then
Exit;
buffer := PAnsiChar(Content);
FillChar(md[0], 1024, 0);
FillChar(hash[0], SHA_DIGEST_LENGTH, 0);
sha1 := LoadFunctionCLib('SHA1');
Assert(@sha1 <> nil, 'SHA1載入失敗');
sha1(buffer, Length(Content), @hash[0]);
rsa_sign := LoadFunctionCLib('RSA_sign');
Assert(@rsa_sign <> nil, 'RSA_sign載入失敗');
FillChar(rsa_out[0], 1024, 0);
rsa_sign(EVP_sha1()._type, @hash[0], SHA_DIGEST_LENGTH, @rsa_out[0], rsa_out_len, r);
SetLength(bytes, rsa_out_len);
if rsa_out_len > 0 then
Move(rsa_out[0], bytes[0], rsa_out_len);
Result := AnsiString(TIdEncoderMIME.EncodeBytes(bytes));
BIO_free(key);
RSA_free(r);
finally
FreeLibrary(hIdCrypto);
UnLoadOpenSSLLibrary
end;
end;

class function TZhiFuBao.GetOrderInfo(PARTNER, SELLER, subject, body, price: string): string;
var
s: string;
begin
// 簽約合作者身份ID
s := 'partner="' + PARTNER + '"';
// 簽約賣家支付寶賬號
s := s + '&seller_id="' + SELLER + '"';
// 商戶網站唯一訂單號
s := s + '&out_trade_no="' + GetTrackNo + '"';
// 商品名稱
s := s + '&subject="' + subject + '"';
// 商品詳情
s := s + '&body="' + body + '"';
// 商品金額
s := s + '&total_fee="' + price + '"';
// 伺服器非同步通知頁面路徑
s := s + '&notify_url="' + 'http://你的業務後臺地址,一定要寫,要合法"';
// 服務介面名稱, 固定值
s := s + '&service="mobile.securitypay.pay"';
// 支付型別, 固定值
s := s + '&payment_type="1"';
// 引數編碼, 固定值
s := s + '&_input_charset="utf-8"';
// 設定未付款交易的超時時間
// 預設30分鐘,一旦超時,該筆交易就會自動被關閉。
// 取值範圍:1m~15d。
// m-分鐘,h-小時,d-天,1c-當天(無論交易何時建立,都在0點關閉)。
// 該引數數值不接受小數點,如1.5h,可轉換為90m。
s := s + '&it_b_pay="30m"';
// extern_token為經過快登授權獲取到的alipay_open_id,帶上此引數使用者將使用授權的賬戶進行支付
// orderInfo += "&extern_token=" + "\"" + extern_token + "\"";
// 支付寶處理完請求後,當前頁面跳轉到商戶指定頁面的路徑,可空
s := s + '&return_url="m.alipay.com"';
// 呼叫銀行卡支付,需配置此引數,參與簽名, 固定值 (需要簽約《無線銀行卡快捷支付》才能使用)
// orderInfo += "&paymethod=\"expressGateway\"";
Result := s;
end;

class function TZhiFuBao.GetTrackNo: string;
var
G: TGuid;
S: string;
begin
G := TGuid.NewGuid;
S := G.ToString;
S := S.Substring(0, 15);
Result := s;
end;

class function TZhiFuBao.SSLHttpPost(URL, OrderInfo: string): string;
var
ssl: TIdSSLIOHandlerSocketOpenSSL;
http: TIdHttp;
List: TStrings;
begin
http := TIdHttp.Create(nil);
http.Request.Accept := 'text/xml,text/javascript,text/html,text/plain';
http.Request.UserAgent := 'aop-sdk-java';
http.HTTPOptions := [hoKeepOrigProtocol, hoForceEncodeParams];
ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
ssl.SSLOptions.Method := sslvTLSv1_2;
http.IOHandler := ssl;
List := TStringList.Create;
List.Delimiter := '&';
List.DelimitedText := OrderInfo; //Builder是形如 auth_code=1234&app_id=qqqqq&charset=utf-8........
try
try
Result := http.Post(URL, List);
// Result := http.Post(URL, List, TEncoding.UTF8);
// TLogger.Instance.Warn('TAliPaySSLHttpPost.Post Result:' + Result);
except
on e: Exception do
begin
Result := '';
ShowMessage(e.Message);
end;
end;
finally
ssl.Free;
http.Free;
List.Free;
end;
end;

class function TZhiFuBao.VerifyRSASign(const PublicKey, Content, Sign: AnsiString): Boolean;
var
buffer: PAnsiChar;
rPub: PRSA;
bioPub: PBIO;
hIdCrypto: HMODULE;
hash: array[0..SHA_DIGEST_LENGTH - 1] of AnsiChar;
type
Tsha1 = function(d: PAnsiChar; n: Cardinal; md: PAnsiChar): PAnsiChar; cdecl;

TRSA_verify = function(dtype: LongInt; const m: PAnsiChar; m_length: LongWord; sigret: PAnsiChar; siglen: Cardinal; const rsa: PRSA): LongInt; cdecl;

TPEM_read_bio_RSA_PUBKEY = function(bp: PBIO; x: PPRSA; cb: Ppem_password_cb; u: Pointer): PRSA; cdecl;

function LoadFunctionCLib(const FceName: string; const ACritical: Boolean = True): Pointer;
begin
Result := GetProcAddress(hIdCrypto, PChar(FceName));
end;

var
sha1: Tsha1;
RSA_verify: TRSA_verify;
bytes: TidBytes;
PEM_read_bio_RSA_PUBKEY: TPEM_read_bio_RSA_PUBKEY;
begin
Result := False;
LoadOpenSSLLibrary;
hIdCrypto := LoadLibrary('libeay32.dll');
Assert(hIdCrypto <> 0, '無法載入libeay32.dll');
try
bioPub := BIO_new_mem_buf(@PublicKey[1], Length(PublicKey));
PEM_read_bio_RSA_PUBKEY := LoadFunctionCLib('PEM_read_bio_RSA_PUBKEY');

Assert(@PEM_read_bio_RSA_PUBKEY <> nil, 'PEM_read_bio_RSA_PUBKEY載入失敗');
rPub := PEM_read_bio_RSA_PUBKEY(bioPub, nil, nil, nil);
if rPub = nil then
Exit;

buffer := PAnsiChar(Content);
FillChar(hash[0], SHA_DIGEST_LENGTH, 0);
bytes := TIdDecoderMIME.DecodeBytes(string(Sign));

sha1 := LoadFunctionCLib('SHA1');
Assert(@sha1 <> nil, 'SHA1載入失敗');
sha1(buffer, Length(Content), @hash[0]);

RSA_verify := LoadFunctionCLib('RSA_verify');
Assert(@RSA_verify <> nil, 'RSA_sign載入失敗');
Result := RSA_verify(EVP_sha1()._type, @hash[0], SHA_DIGEST_LENGTH, @bytes[0], Length(bytes), rPub) > 0;
BIO_free(bioPub);
RSA_free(rPub);
finally
FreeLibrary(hIdCrypto);
UnLoadOpenSSLLibrary
end;
end;

end.