欢迎您光临深圳塔灯网络科技有限公司!
电话图标 余先生:13699882642

网站百科

为您解码网站建设的点点滴滴

支付宝开放平台C++方式接入

发表日期:2019-08 文章编辑:小灯 浏览次数:867


#include <iostream>
#include "stdlib.h"

#define _DEBUG

#include "openapi/openapi_client.h"

using namespace std;

/** ++++++++++++++++++++++++++++++++++++++++++++++++ **/
/** 此处替换为开发者在支付宝开放平台申请的应用ID **/
string appId = "2016000066668888";

/** 此处替换为开发者使用openssl生成的rsa私钥 **/
string pKey = "-----BEGIN RSA PRIVATE KEY-----\n"
		"MIICXQIBAAKBgQDTI0/RaV/YKWDGbKkQGYpD9I/UljBCCf3rWm09sXiif8MN5rLA\n"
		"3TjC4gZ478n6Dys5yO23h1HGVTWu+mQ8071+pwLHGQ+dyDNrGWR89VLb9yanOeRf\n"
		"efOcN19ATZgGAzheM28E/iqaYkh8F2NlCjOiZAsBG6eVvxachwVAQUIWwwIDAQAB\n"
		"AoGAdVr8Q46JenHNW50L/2niw1DNHUF5g0tgeo+hhpf9UH0pIrHnC3Iq2Y+eP1ww\n"
		"7K+/u/elwcwSNOYp159PVcvvV9LwPwH29DdH6KEWIDiyFpjbXPcMMFwgakyLnFTL\n"
		"sxxa6DYznFokT+IPkF6esoypa7VQFU1RIal5Sgphq7CGCDECQQDqyL3QjYT6ffLd\n"
		"NRiMBB13+eIxvXGy5AEQcH4pNt6kYHWONCWeZ34miNp2UliIBvBHZ1uuGoO4F/Jx\n"
		"2sWwWlSpAkEA5jeQGFx/RDzzi0qPMpSOR50d2IC4NbbresY+hgJEBbI6n5hPR1ts\n"
		"MUuO1e3L5I5rzRKNzD1um1DdSgmqaqmHiwJBANLnRpNsPRMjRqHtS0Kjg7E9mDIk\n"
		"Qll3NXmGA96T+oXgXFlEgLJ9tzV4Y/471GlFClyp/RI1oTMi19fstP7I9hkCQBjr\n"
		"bseUS5phVqN/QJzjA7uwwChNVqNJ15eEmgP7fs13C213GS3KMZ3sZdu2T9m/qN+b\n"
		"4Il5JN3fFPUMssu06h0CQQCGBdmtRLi+9ws57qTPHR/BdHGUxdBRWllc9sGVVaRw\n"
		"+EOMGXus6/BssTRjwplx7w8uUR0U3s1KYDJMHMCjW25x\n"
		"-----END RSA PRIVATE KEY-----";

/** 支付宝公钥,用来验证支付宝返回请求的合法性 **/
string aliPubKey = "-----BEGIN PUBLIC KEY-----\n"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkr\n"
"IvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsra\n"
"prwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUr\n"
"CmZYI/FCEa3/cNMW0QIDAQAB\n"
"-----END PUBLIC KEY-----";

/** 注:appid,私钥,支付宝公钥等信息建议不要写死在代码中 **/
/** 这些信息应以配置等方式保存,此处写在代码中只是为了示例的简便 **/
/** ++++++++++++++++++++++++++++++++++++++++++++++++ **/


/** some examples **/
JsonMap getPrecreateContent();

int main(int argc, char *argv[])
{

/** 实例化OpenapiClient工具类 **/
OpenapiClient openapiClient(appId,
pKey,
OpenapiClient::default_url,
OpenapiClient::default_charset,
aliPubKey);

/** ++++++++++++++++++++++++++++++++++++++++++++++++ **/
/** 各个具体业务接口参数组装模式具体参看Openapi官方文档 **/
/** https://doc.open.alipay.com/ **/
// demo1:当面付预下单示例
string method = "alipay.trade.precreate";
JsonMap contentMap = getPrecreateContent();

/** ++++++++++++++++++++++++++++++++++++++++++++++++ **/

/** ++++++++++++++++++++++++++++++++++++++++++++++++ **/
/** 网关扩展参数,例如商户需要回传notify_url等,可以在extendParamMap中传入 **/
/** 这是一个可选项,如不需要,可不传 **/
/* StringMap extendParamMap;
extendParamMap.insert(StringMap::value_type("notify_url", "http://api.test.alipay.net/atinterface/receive_notify.htm"));
*/

/** ++++++++++++++++++++++++++++++++++++++++++++++++ **/

/** 调用Openapi网关 **/
JsonMap respMap;
respMap = openapiClient.invoke(method, contentMap);
/* 如果有扩展参数,则按如下方式传入
respMap = openapiClient.invoke(method, contentMap, extendParamMap);
*/

/** 解析支付宝返回报文 **/
JsonMap::const_iterator iter = respMap.find("code");
if (iter != respMap.end()) {
string respCode = iter->second.toString();
DebugLog("code:%s", respCode.c_str());
} else {
DebugLog("cannot get code from response");
}

iter = respMap.find("msg");
if (iter != respMap.end()) {
string respMsg = iter->second.toString();
DebugLog("msg:%s", respMsg.c_str());
} else {
DebugLog("cannot get msg from response");
}

system("pause");
return 0;
}

/**
 * 组装支付宝预下单业务请求
 */
JsonMap getPrecreateContent() {

JsonMap contentMap;
contentMap.insert(JsonMap::value_type(JsonType("out_trade_no"), JsonType("20160606121212")));
contentMap.insert(JsonMap::value_type(JsonType("total_amount"), JsonType(0.01)));
contentMap.insert(JsonMap::value_type(JsonType("subject"), JsonType("好东西")));

return contentMap;
}


该项目为C++项目,包含访问支付宝开放平台(Openapi)网关的源码;

/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ **/

工程中有两个项目文件:
Openapi.pro为QtCreater项目文件,可用QtCreater打开;
Openapi.sln为VS(2008版)项目文件,可用VS打开;

注:该项目源码依赖C\C++标准库(STL)以及几个开源的第三方库(cJSON,libcurl,openssl);
开发者可以在其它支持C\C++的平台(linux等)编译适配;

/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ **/

项目主要源码包含在目录“/openapi”中;

1)主工具类 -- ./openapi/openapi_client 
---- 访问支付宝开放平台(openapi)网关的工具类;
---- 外部主要依赖该类访问支付宝网关;
---- 具体使用示例可参见main.cpp中的源码及注释;

2)其它依赖的工具类:
HttpClient -- ./openapi/http/http_client
---- 该工具类提供Http(Https)网络通信的功能;
---- 该工具类依赖第三方库libcurl

JsonUtil -- ./openapi/json/json_util
---- 该工具类提供Json串与C++对象之间的转换功能;
---- 该工具类依赖第三方库cJSON;

openssl & libcurl -- ./libs/
---- 这两个第三方库已经编译成静态库(windows平台);
---- 其它平台的静态库,开发者可自行下载源码进行编译;

/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ **/

集成/调试之前的准备工作:

1)入住支付宝开放平台,申请应用并获得应用ID(appId);

2)本地生成rsa密钥对,并上传公钥到支付宝开放平台相应的应用下,
 并保存好自己的私钥(privateKey -- 严格保密);
 rsa密钥生成方式参看支付宝开放平台官方文档;
 注:C++使用的是标准格式的rsa私钥,不是pkcs8格式的;
	
3)各个具体业务接口参数组装模式具体参看Openapi官方文档;

4)支付宝开放平台文档中心:https://doc.open.alipay.com/


#ifndef OPENAPICLIENT_H
#define OPENAPICLIENT_H

#include "http/http_client.h"
#include "json/json_util.h"
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <time.h>

#ifndef XRSA_KEY_BITS
#define XRSA_KEY_BITS (1024)
#endif

/** STL map default sort order by key **/
typedef map<string, string> StringMap;

/**
 * @brief The OpenapiClient class
 */
class OpenapiClient {
public:
OpenapiClient(const string &appId,
const string &privateKey,
const string &url = default_url,
const string &charset = default_charset,
const string &alipayPublicKey = string());

public:
static const string default_charset;
static const string default_url;
static const string default_sign_type;
static const string default_version;

static const string KEY_APP_ID;
static const string KEY_METHOD;
static const string KEY_CHARSET;
static const string KEY_SIGN_TYPE;
static const string KEY_SIGN;
static const string KEY_TIMESTAMP;
static const string KEY_VERSION;
static const string KEY_BIZ_CONTENT;

private:
string appId;
string privateKey;
string signType;
string version;
string charset;
string url;

string alipayPublicKey;

public:
string invoke(const string &method, const string &content, const StringMap &extendParamMap = StringMap());
JsonMap invoke(const string &method, const JsonMap &contentMap, const StringMap &extendParamMap = StringMap());


private:
/**
 *
 * STL map default sort order by key
 *
 * STL map 默认按照key升序排列
 * 这里要注意如果使用的map必须按key升序排列
 *
 */
string buildContent(const StringMap &contentPairs);
string analyzeResponse(const string &responseStr);

public:
static string base64Encode(const unsigned char *bytes, int len);
static bool base64Decode(const string &str, unsigned char *bytes, int &len);

static string rsaSign(const string &content, const string &key);
static bool rsaVerify(const string &content, const string &sign, const string &key);

public:
string getAppId();

string getSignType();

string getVersion();

string getCharset();

string getUrl();

string getAlipayPublicKey();

};

#endif // OPENAPICLIENT_H


#include "openapi_client.h"


const string OpenapiClient::default_charset= "utf-8";
const string OpenapiClient::default_url= "https://openapi.alipay.com/gateway.do";
const string OpenapiClient::default_sign_type= "RSA";
const string OpenapiClient::default_version= "2.0";

const string OpenapiClient::KEY_APP_ID = "app_id";
const string OpenapiClient::KEY_METHOD = "method";
const string OpenapiClient::KEY_CHARSET= "charset";
const string OpenapiClient::KEY_SIGN_TYPE= "sign_type";
const string OpenapiClient::KEY_SIGN = "sign";
const string OpenapiClient::KEY_TIMESTAMP= "timestamp";
const string OpenapiClient::KEY_VERSION= "version";
const string OpenapiClient::KEY_BIZ_CONTENT= "biz_content";


OpenapiClient::OpenapiClient(const string &appId,
 const string &privateKey,
 const string &url,
 const string &charset,
 const string &alipayPublicKey)
: appId(appId),
privateKey(privateKey),
signType(default_sign_type),
version(default_version),
url(url),
charset(charset),
alipayPublicKey(alipayPublicKey) {

}

JsonMap OpenapiClient::invoke(const string &method, const JsonMap &contentMap, const StringMap &extendParamMap) {

string content = JsonUtil::objectToString(JsonType(contentMap));
string responseContent = invoke(method, content, extendParamMap);
JsonType jsonObj = JsonUtil::stringToObject(responseContent);
return jsonObj.toMap();
}

string OpenapiClient::invoke(const string &method, const string &content, const StringMap &extendParamMap) {

time_t t = time(0);
char tmp[64];
strftime(tmp, sizeof(tmp), "%Y-%m-%d %X", localtime(&t));

StringMap requestPairs;
requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_APP_ID, appId));
requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_BIZ_CONTENT, content));
requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_CHARSET, charset));
requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_METHOD, method));
requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_SIGN_TYPE, signType));
requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_TIMESTAMP, tmp));
requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_VERSION, version));

/** 追加外部传入的网关的补充参数,如notify_url等 **/
for (StringMap::const_iterator iter = extendParamMap.begin(); iter != extendParamMap.end(); ++iter) {
requestPairs.insert(StringMap::value_type(iter->first, iter->second));
}

string wholeContent = buildContent(requestPairs);
string sign = OpenapiClient::rsaSign(wholeContent, privateKey);
requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_SIGN, sign));

wholeContent = buildContent(requestPairs);
DebugLog("Request:%s", wholeContent.c_str());

HttpClient httpClient;
string responseStr = httpClient.sendSyncRequest(url, requestPairs);

DebugLog("Response:%s", responseStr.c_str());

string responseContent = analyzeResponse(responseStr);
return responseContent;
}

/**
 *
 * STL map default sort order by key
 *
 * STL map 默认按照key升序排列
 * 这里要注意如果使用的map必须按key升序排列
 *
 */
string OpenapiClient::buildContent(const StringMap &contentPairs) {

string content;
for (StringMap::const_iterator iter = contentPairs.begin();
 iter != contentPairs.end(); ++iter) {
if (!content.empty()) {
content.push_back('&');
}
content.append(iter->first);
content.push_back('=');
content.append(iter->second);
}
return content;
}

string OpenapiClient::analyzeResponse(const string &responseStr) {

JsonType responseObj = JsonUtil::stringToObject(responseStr);
JsonMap responseMap = responseObj.toMap();
//获取返回报文中的alipay_xxx_xxx_response的内容;
int beg = responseStr.find("_response\"");
int end = responseStr.rfind("\"sign\"");
if (beg < 0 || end < 0) {
return string();
}
beg = responseStr.find('{', beg);
end = responseStr.rfind('}', end);
//注意此处将map转为json之后的结果需要与支付宝返回报文中原格式与排序一致;
//排序规则是节点中的各个json节点key首字母做字典排序;
//Response的Json值内容需要包含首尾的“{”和“}”两个尖括号,双引号也需要参与验签;
//如果字符串中包含“http://”的正斜杠,需要先将正斜杠做转义,默认打印出来的字符串是已经做过转义的;
//此处转换之后的json字符串默认为"Compact"模式,即紧凑模式,不要有空格与换行;
string responseContent = responseStr.substr(beg, end - beg + 1);

DebugLog("ResponseContent:%s", responseContent.c_str());

//此处为校验支付宝返回报文中的签名;
//如果支付宝公钥为空,则默认跳过该步骤,不校验签名;
//如果支付宝公钥不为空,则认为需要校验签名;
if (!alipayPublicKey.empty()) {

DebugLog("AlipayPublicKey:%s", alipayPublicKey.c_str());

JsonMap::const_iterator iter = responseMap.find(OpenapiClient::KEY_SIGN);
if (iter == responseMap.end()) {
DebugLog("Cannot get Sign from response, Verify Failed");
return string();
}
//获取返回报文中的sign;
string responseSign = iter->second.toString();

DebugLog("ResponseSign:%s", responseSign.c_str());

//调用验签方法;
bool verifyResult = OpenapiClient::rsaVerify(responseContent, responseSign, alipayPublicKey);

if (!verifyResult) {
DebugLog("Verify Failed");
return string();
}
DebugLog("Verify Success");
} else {
DebugLog("AlipayPublicKey is empty, Skip the Verify");
}

return responseContent;
}

string OpenapiClient::rsaSign(const string &content, const string &key) {

string signed_str;
const char *key_cstr = key.c_str();
int key_len = strlen(key_cstr);
BIO *p_key_bio = BIO_new_mem_buf((void *)key_cstr, key_len);
RSA *p_rsa = PEM_read_bio_RSAPrivateKey(p_key_bio, NULL, NULL, NULL);

if (p_rsa != NULL) {

const char *cstr = content.c_str();
unsigned char hash[SHA_DIGEST_LENGTH] = {0};
SHA1((unsigned char *)cstr, strlen(cstr), hash);
unsigned char sign[XRSA_KEY_BITS / 8] = {0};
unsigned int sign_len = sizeof(sign);
int r = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, sign, &sign_len, p_rsa);

if (0 != r && sizeof(sign) == sign_len) {
signed_str = base64Encode(sign, sign_len);
}
}

RSA_free(p_rsa);
BIO_free(p_key_bio);
return signed_str;
}

bool OpenapiClient::rsaVerify(const string &content, const string &sign, const string &key) {

bool result = false;
const char *key_cstr = key.c_str();
int key_len = strlen(key_cstr);
BIO *p_key_bio = BIO_new_mem_buf((void *)key_cstr, key_len);
RSA *p_rsa = PEM_read_bio_RSA_PUBKEY(p_key_bio, NULL, NULL, NULL);

if (p_rsa != NULL) {
const char *cstr = content.c_str();
unsigned char hash[SHA_DIGEST_LENGTH] = {0};
SHA1((unsigned char *)cstr, strlen(cstr), hash);
unsigned char sign_cstr[XRSA_KEY_BITS / 8] = {0};
int len = XRSA_KEY_BITS / 8;
base64Decode(sign, sign_cstr, len);
unsigned int sign_len = XRSA_KEY_BITS / 8;
int r = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, (unsigned char *)sign_cstr, sign_len, p_rsa);

if (r > 0) {
result = true;
}
}

RSA_free(p_rsa);
BIO_free(p_key_bio);
return result;
}

string OpenapiClient::base64Encode(const unsigned char *bytes, int len) {

BIO *bmem = NULL;
BIO *b64 = NULL;
BUF_MEM *bptr = NULL;

b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, bytes, len);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);

string str = string(bptr->data, bptr->length);
BIO_free_all(b64);
return str;
}

bool OpenapiClient::base64Decode(const string &str, unsigned char *bytes, int &len) {

const char *cstr = str.c_str();
BIO *bmem = NULL;
BIO *b64 = NULL;

b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf((void *)cstr, strlen(cstr));
b64 = BIO_push(b64, bmem);
len = BIO_read(b64, bytes, len);

BIO_free_all(b64);
return len > 0;
}


string OpenapiClient::getAppId() {
return appId;
}

string OpenapiClient::getSignType() {
return signType;
}

string OpenapiClient::getVersion() {
return version;
}

string OpenapiClient::getCharset() {
return charset;
}

string OpenapiClient::getUrl() {
return url;
}

string OpenapiClient::getAlipayPublicKey() {
return alipayPublicKey;
}

本页内容由塔灯网络科技有限公司通过网络收集编辑所得,所有资料仅供用户学习参考,本站不拥有所有权,如您认为本网页中由涉嫌抄袭的内容,请及时与我们联系,并提供相关证据,工作人员会在5工作日内联系您,一经查实,本站立刻删除侵权内容。本文链接:http://www.dengtar.com/20745.html
相关企业建站知识
 八年  行业经验

多一份参考,总有益处

联系深圳网站公司塔灯网络,免费获得网站建设方案及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

业务热线:余经理:13699882642

Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.