#include "StdAfx.h" #include "PayApi.h" #include #include "EncodingConversion.h" // 支付宝公共参数; const string Alipay_default_charset = "utf-8"; const string Alipay_default_url = "https://openapi.alipay.com/gateway.do"; const string Alipay_default_sign_type = "RSA"; const string Alipay_default_version = "1.0"; const string Alipay_appid = "app_id"; const string Alipay_method = "method"; const string Alipay_charset = "charset"; const string Alipay_signtype = "sign_type"; const string Alipay_sign = "sign"; const string Alipay_timestamp = "timestamp"; const string Alipay_version = "version"; const string Alipay_bizcontenr = "biz_content"; PayApi::PayApi(void) { } PayApi::~PayApi(void) { } /************************************************************************/ /* 函数:base64Encode[3/30/2017 Jeff]; /* 描述:将字符串转成base64格式; /* 参数:; /* [IN] bytes:要转的字符串指针; /* [IN] len:字符串长度; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ string PayApi::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; } /************************************************************************/ /* 函数:base64Decode[3/30/2017 Jeff]; /* 描述:解码base64字符串; /* 参数:; /* [IN] str:要解码的base64字符串; /* [OUT] bytes:存储解码结果的字符串缓存指针; /* [OUT] len:解码后的字符串长度; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ bool PayApi::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; } /************************************************************************/ /* 函数:rsaSign[3/30/2017 Jeff]; /* 描述:rsa签名; /* 参数:; /* [IN] content:要使用rsa签名的字符串; /* [IN] key:rsa签名的密钥; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ string PayApi::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; } /************************************************************************/ /* 函数:rsaVerify[3/30/2017 Jeff]; /* 描述:rsa签名验证; /* 参数:; /* [IN] content:要验证的字符串; /* [IN] sign:验证比较的rsa签名; /* [IN] key:rsa签名的密钥; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ bool PayApi::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; } /************************************************************************/ /* 函数:alipay_setCommonParam[3/30/2017 Jeff]; /* 描述:设置支付宝支付的公共参数; /* 参数:; /* [IN] appid:支付宝分配开发者的应用ID; /* [IN] privatekey:私钥; /* [IN] signtype:签名方式; /* [IN] charset:字符集; /* [IN] version:版本; /* [IN] url:主url; /* [IN] publickey:公钥; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ void PayApi::alipay_setCommonParam( const string &appid, const string &privatekey, const string &signtype, const string &charset, const string &version, const string &url, const string &publickey ) { alipay_appid = appid; alipay_privatekey = privatekey; alipay_signtype = signtype; alipay_charset = charset; alipay_version = version; alipay_url = url; alipay_PublicKey = publickey; } /************************************************************************/ /* 函数:[3/30/2017 Jeff]; /* 描述:; /* 参数:; /* [IN] :; /* [OUT] :; /* [IN/OUT] :; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ string PayApi::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; } /************************************************************************/ /* 函数:[3/30/2017 Jeff]; /* 描述:; /* 参数:; /* [IN] :; /* [OUT] :; /* [IN/OUT] :; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ string PayApi::alipay_invoke(const string & method, const JsonMap & contentMap, const StringMap & extendParamMap) { string content = JsonUtil::objectToString(JsonType(contentMap)); return alipay_invoke(method, content, extendParamMap); } /************************************************************************/ /* 函数:[3/30/2017 Jeff]; /* 描述:; /* 参数:; /* [IN] :; /* [OUT] :; /* [IN/OUT] :; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ JsonMap PayApi::alipay_Str2JsonMap(string strJson) { //获取返回报文中的alipay_xxx_xxx_response的内容; int beg = strJson.find("_response\""); int end = strJson.rfind("\"sign\""); if (beg < 0 || end < 0) { JsonType jsonObj = JsonUtil::stringToObject(strJson); return jsonObj.toMap(); } beg = strJson.find('{', beg); end = strJson.rfind('}', end); strJson = strJson.substr(beg, end - beg + 1); JsonType jsonObj = JsonUtil::stringToObject(strJson); return jsonObj.toMap(); } /************************************************************************/ /* 函数:[3/31/2017 Jeff]; /* 描述:; /* 参数:; /* [IN] :; /* [OUT] :; /* [IN/OUT] :; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ string PayApi::alipay_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(Alipay_appid, alipay_appid)); requestPairs.insert(StringMap::value_type(Alipay_bizcontenr, content)); requestPairs.insert(StringMap::value_type(Alipay_charset, alipay_charset)); requestPairs.insert(StringMap::value_type(Alipay_method, method)); requestPairs.insert(StringMap::value_type(Alipay_signtype, alipay_signtype)); requestPairs.insert(StringMap::value_type(Alipay_timestamp, tmp)); requestPairs.insert(StringMap::value_type(Alipay_version, alipay_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 = rsaSign(wholeContent, alipay_privatekey); requestPairs.insert(StringMap::value_type(Alipay_sign, sign)); wholeContent = buildContent(requestPairs); #ifdef _DEBUG WriteTextLog("\r\nRequest:%s\r\n", wholeContent.c_str()); #endif HttpClient httpClient; string responseStr = httpClient.sendSyncRequest(alipay_url, requestPairs); #ifdef _DEBUG WriteTextLog("\r\nResponse:%s\r\n", responseStr.c_str()); #endif // 验签失败原因未找到,暂时不处理; //string responseContent = analyzeAliResponse(responseStr); //return responseContent; return responseStr; } string PayApi::analyzeAliResponse(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 (!alipay_PublicKey.empty()) { DebugLog("AlipayPublicKey:%s", alipay_PublicKey.c_str()); JsonMap::const_iterator iter = responseMap.find(Alipay_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 = rsaVerify(responseContent, responseSign, alipay_PublicKey); if (!verifyResult) { DebugLog("Verify Failed"); return string(); } DebugLog("Verify Success"); } else { DebugLog("AlipayPublicKey is empty, Skip the Verify"); } return responseContent; } /************************************************************************/ /* 函数:[3/31/2017 Jeff]; /* 描述:; /* 参数:; /* [IN] :; /* [OUT] :; /* [IN/OUT] :; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ void PayApi::wxpay_setCommonParam(const string & appid, const string & mchid, const string & privatekey) { wxpay_appid = appid; wxpay_mchid = mchid; wxpay_privatekey = privatekey; } /************************************************************************/ /* 函数:[3/30/2017 Jeff]; /* 描述:; /* 参数:; /* [IN] :; /* [OUT] :; /* [IN/OUT] :; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ string PayApi::wxapi_getSign(StringMap &strMap, string key) { string strtemp; vector vtString; for (StringMap::const_iterator iter = strMap.begin(); iter != strMap.end(); iter++) { if ( _tcscmp(iter->first.c_str(), "sign") != 0 && iter->second.size() != 0 ) {// 除去sign本身; strtemp = iter->first; strtemp.append("="); strtemp.append(iter->second); vtString.push_back(strtemp); } } strtemp.clear(); std::sort(vtString.begin(), vtString.end()); for (vector::iterator it = vtString.begin(); it != vtString.end(); it++ ) { strtemp.append(it->c_str()); strtemp.append("&"); } strtemp.append("key="); strtemp.append(key); #ifdef _DEBUG WriteTextLog("合并:%s\n", strtemp.c_str()); #endif // 计算MD5,必须使用utf-8来计算; EncodingConverion::ASCII2UTF8(strtemp.c_str(), strtemp); // md5加密; MD5_CTX ctx; char sztmp[3] = {0}; unsigned char szmd[16] = {0}; MD5_Init(&ctx); MD5_Update(&ctx, strtemp.data(), strtemp.size()); MD5_Final(szmd, &ctx); strtemp.clear(); for ( int i = 0; i < 16; i++ ) { sprintf_s(sztmp, "%02X", szmd[i]); strtemp.append(sztmp); } #ifdef _DEBUG WriteTextLog("MD5:%s\n", strtemp.c_str()); #endif return strtemp; } /************************************************************************/ /* 函数:[3/30/2017 Jeff]; /* 描述:; /* 参数:; /* [IN] :; /* [OUT] :; /* [IN/OUT] :; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ string PayApi::wxpay_invoke(const string &method, StringMap &extendParamMap, string key ) { // 计算出签名; string sign = wxapi_getSign(extendParamMap, key); // 将签名加入; extendParamMap.insert(StringMap::value_type("sign", sign)); tinyxml2::XMLDocument doc; tinyxml2::XMLNode *node = doc.InsertEndChild(doc.NewElement("xml")); tinyxml2::XMLElement *subelement = NULL; for (StringMap::const_iterator iter = extendParamMap.begin(); iter != extendParamMap.end(); ++iter) { subelement = doc.NewElement(iter->first.c_str()); subelement->SetText(iter->second.c_str()); node->InsertEndChild(subelement); } tinyxml2::XMLPrinter printer; doc.Print(&printer); string result = printer.CStr(); EncodingConverion::ASCII2UTF8(result.c_str(), result); HttpClient httpClient; string responseStr = httpClient.sendSyncRequest(method, result); EncodingConverion::UTF82ASCII(responseStr.c_str(), responseStr); #ifdef _DEBUG WriteTextLog("返回码:%s\n", responseStr.c_str()); #endif DebugLog("Response:%s", responseStr.c_str()); return responseStr; } /************************************************************************/ /* 函数:[3/30/2017 Jeff]; /* 描述:; /* 参数:; /* [IN] :; /* [OUT] :; /* [IN/OUT] :; /* 返回:void; /* 注意:; /* 示例:; /* /* 修改:; /* 日期:; /* 内容:; /************************************************************************/ string PayApi::analyzeWXRespone(const string &respone) { return ""; }