链接:/ArthasModern/AlipayOpenapiCpp
如果你想以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 = "000066668888";/** 此处替换为开发者使用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./ **/// demo1:当面付预下单示例string method = "alipay.trade.precreate";JsonMap contentMap = getPrecreateContent();/** ++++++++++++++++++++++++++++++++++++++++++++++++ **//** ++++++++++++++++++++++++++++++++++++++++++++++++ **//** 网关扩展参数,例如商户需要回传notify_url等,可以在extendParamMap中传入 **//** 这是一个可选项,如不需要,可不传 **//* StringMap extendParamMap;extendParamMap.insert(StringMap::value_type("notify_url", "http://api./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("0606121212")));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(版)项目文件,可用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)网络通信的功能;---- 该工具类依赖第三方库libcurlJsonUtil -- ./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./
其中关键工具类的构建如下:
#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= "/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;}