支付宝开放平台C++方式接入
2016-07-08 13:56
519 查看
链接:https://github.com/ArthasModern/AlipayOpenapiCpp
如果你想以C\C++方式接入支付宝开放平台调用支付宝的各种开放接口(比如收款等),那么你可以参考这个项目;
这个项目里帮你解决了参数组装、排序、OpenSSL加签验签、网络请求等问题,能帮助你轻松接入支付宝;
并且该项目依赖C\C++标准库STL以及几个开源的第三方库,代码精简(只有几个class),非常易于集成;
使用起来很简便:
Readme.txt:
其中关键工具类的构建如下:
源文件:
如果你想以C\C++方式接入支付宝开放平台调用支付宝的各种开放接口(比如收款等),那么你可以参考这个项目;
这个项目里帮你解决了参数组装、排序、OpenSSL加签验签、网络请求等问题,能帮助你轻松接入支付宝;
并且该项目依赖C\C++标准库STL以及几个开源的第三方库,代码精简(只有几个class),非常易于集成;
使用起来很简便:
#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; }
Readme.txt:
该项目为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; }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- OpenSSL编程之RSA
- 关于指针的一些事情
- 怎样安装openssl 2011-12-11
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例