发表日期: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;
}
日期:2019-02 浏览次数:6403
日期:2019-02 浏览次数:4162
日期:2019-02 浏览次数:3907
日期:2019-02 浏览次数:4363
日期:2019-02 浏览次数:4093
日期:2019-02 浏览次数:7218
日期:2019-02 浏览次数:3951
日期:2019-02 浏览次数:4435
日期:2019-02 浏览次数:4284
日期:2019-02 浏览次数:3970
日期:2019-02 浏览次数:4304
日期:2019-02 浏览次数:4075
日期:2019-02 浏览次数:3974
日期:2019-02 浏览次数:3897
日期:2019-02 浏览次数:4642
日期:2019-02 浏览次数:4098
日期:2019-02 浏览次数:4088
日期:2019-02 浏览次数:4148
日期:2019-02 浏览次数:4107
日期:2019-02 浏览次数:4019
日期:2019-02 浏览次数:4384
日期:2019-09 浏览次数:4405
日期:2019-09 浏览次数:4058
日期:2019-09 浏览次数:4076
日期:2019-09 浏览次数:4028
日期:2019-09 浏览次数:4006
日期:2019-09 浏览次数:4100
日期:2019-09 浏览次数:4045
日期:2019-09 浏览次数:4516
日期:2019-09 浏览次数:5030
Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.