WeChatFunction.cs 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. using System;
  2. using System.Collections.Generic;
  3. using Library;
  4. using LitJson;
  5. using System.Linq;
  6. using System.Security.Cryptography.X509Certificates;
  7. using System.Security.Cryptography;
  8. using System.Text;
  9. using System.IO;
  10. using System.Net;
  11. using Org.BouncyCastle.Crypto.Modes;
  12. using Org.BouncyCastle.Crypto.Parameters;
  13. using Org.BouncyCastle.Crypto.Engines;
  14. namespace MySystem
  15. {
  16. public class WeChatFunction
  17. {
  18. public readonly static WeChatFunction Instance = new WeChatFunction();
  19. private WeChatFunction()
  20. { }
  21. public string AppId = "wx5417e0770bb19c4e";
  22. public string MchId = "1611167423";
  23. public string serialNo = "534165421BE2814DEC08F8D00BDAB9944B13646C";
  24. #region 服务商平台商户进件提交申请单API
  25. public string MerchantCreate(PxcModels.MerchantAddInfo info, PxcModels.MerchantInfo merchant)
  26. {
  27. // pubkey = GetPublicKey();
  28. Dictionary<string, object> result = new Dictionary<string, object>();
  29. result.Add("business_code", info.BusinessCode); //业务申请编号
  30. PxcModels.WebCMSEntities dbpxc = new PxcModels.WebCMSEntities();
  31. //超级管理员信息
  32. Dictionary<string, object> contact_info = new Dictionary<string, object>();
  33. contact_info.Add("contact_type", "LEGAL"); //超级管理员姓名
  34. contact_info.Add("contact_name", RSAEncrypt(info.ContactName)); //超级管理员姓名
  35. contact_info.Add("contact_id_number", RSAEncrypt(info.ContactIdNumber)); //超级管理员身份证件号码
  36. // contact_info.Add("openid", info.OpenId); //超级管理员微信openid
  37. contact_info.Add("mobile_phone", RSAEncrypt(info.MobilePhone)); //联系手机
  38. contact_info.Add("contact_email", RSAEncrypt(info.ContactEmail)); //联系邮箱
  39. result.Add("contact_info", contact_info);
  40. //主体资料
  41. Dictionary<string, object> subject_info = new Dictionary<string, object>();
  42. subject_info.Add("subject_type", info.SubjectType); //主体类型
  43. Dictionary<string, object> business_license_info = new Dictionary<string, object>();
  44. business_license_info.Add("license_copy", GetMediaId("/" + info.LicenseCopy)); //营业执照照片
  45. business_license_info.Add("license_number", info.LicenseNumber); //注册号/统一社会信用代码
  46. business_license_info.Add("merchant_name", merchant.Name); //商户名称
  47. business_license_info.Add("legal_person", info.LegalPerson); //个体户经营者/法人姓名
  48. subject_info.Add("business_license_info", business_license_info); //营业执照
  49. // Dictionary<string, object> certificate_info = new Dictionary<string, object>();
  50. // certificate_info.Add("cert_copy", GetMediaId("/" + info.CertCopy)); //登记证书照片
  51. // certificate_info.Add("cert_type", info.CertType); //登记证书类型
  52. // certificate_info.Add("cert_number", info.CertNumber); //证书号
  53. // certificate_info.Add("merchant_name", info.CertMerchantName); //商户名称
  54. // certificate_info.Add("company_address", info.CompanyAddress); //注册地址
  55. // certificate_info.Add("legal_person", info.CertLegalPerson); //法人姓名
  56. // certificate_info.Add("period_begin", CheckForever(info.PeriodBegin)); //有效期限开始日期
  57. // certificate_info.Add("period_end", CheckForever(info.PeriodEnd)); //有效期限结束日期
  58. // subject_info.Add("certificate_info", certificate_info); //登记证书
  59. // Dictionary<string, object> organization_info = new Dictionary<string, object>();
  60. // organization_info.Add("organization_copy", GetMediaId("/" + info.OrganizationCopy)); //组织机构代码证照片
  61. // organization_info.Add("organization_code", info.OrganizationCode); //组织机构代码
  62. // organization_info.Add("org_period_begin", CheckForever(info.OrgPeriodBegin)); //组织机构代码证有效期开始日期
  63. // organization_info.Add("org_period_end", CheckForever(info.OrgPeriodEnd)); //组织机构代码证有效期结束日期
  64. // subject_info.Add("organization_info", organization_info); //组织机构代码证
  65. // subject_info.Add("certificate_letter_copy", GetMediaId("/" + info.CertificateLetterCopy)); //单位证明函照片
  66. Dictionary<string, object> identity_info = new Dictionary<string, object>();
  67. info.IdDocType = "IDENTIFICATION_TYPE_IDCARD";
  68. identity_info.Add("id_holder_type", "LEGAL"); //证件持有人类型
  69. identity_info.Add("id_doc_type", info.IdDocType); //证件类型
  70. Dictionary<string, object> id_card_info = new Dictionary<string, object>();
  71. id_card_info.Add("id_card_copy", GetMediaId("/" + info.IdCardCopy)); //身份证人像面照片
  72. id_card_info.Add("id_card_national", GetMediaId("/" + info.IdCardNational)); //身份证人像面照片
  73. id_card_info.Add("id_card_name", RSAEncrypt(info.IdCardName)); //身份证姓名
  74. id_card_info.Add("id_card_number", RSAEncrypt(info.IdCardNumber)); //身份证号码
  75. id_card_info.Add("id_card_address", RSAEncrypt(info.IdCardAddress)); //身份证居住地址
  76. id_card_info.Add("card_period_begin", CheckForever(info.CardPeriodBegin)); //身份证有效期开始时间
  77. id_card_info.Add("card_period_end", CheckForever(info.CardPeriodEnd)); //身份证有效期结束时间
  78. identity_info.Add("id_card_info", id_card_info); //身份证信息
  79. // identity_info.Add("owner", info.Owner == 1 ? true : false); //经营者/法人是否为受益人
  80. identity_info.Add("owner", true);
  81. subject_info.Add("identity_info", identity_info); //经营者/法人身份证件
  82. // if (info.Owner == 0)
  83. // {
  84. // Dictionary<string, object> ubo_info = new Dictionary<string, object>();
  85. // ubo_info.Add("id_type", info.UboInfoIdType); //证件类型
  86. // ubo_info.Add("id_card_copy", GetMediaId("/" + info.UboInfoIdCardCopy)); //身份证人像面照片
  87. // ubo_info.Add("id_card_national", GetMediaId("/" + info.UboInfoIdCardNational)); //身份证国徽面照片
  88. // ubo_info.Add("id_doc_copy", GetMediaId("/" + info.UboInfoIdDocCopy)); //证件照片
  89. // ubo_info.Add("name", RSAEncrypt(info.UboInfoName)); //受益人姓名
  90. // ubo_info.Add("id_number", RSAEncrypt(info.UboInfoIdNumber)); //证件号码
  91. // ubo_info.Add("id_period_begin", CheckForever(info.UboInfoIdPeriodBegin)); //证件有效期开始时间
  92. // ubo_info.Add("id_period_end", CheckForever(info.UboInfoIdPeriodEnd)); //证件有效期结束时间
  93. // subject_info.Add("ubo_info", ubo_info); //最终受益人信息(UBO)
  94. // }
  95. result.Add("subject_info", subject_info);
  96. //经营资料
  97. Dictionary<string, object> business_info = new Dictionary<string, object>();
  98. business_info.Add("merchant_shortname", info.MerchantShortname); //商户简称
  99. business_info.Add("service_phone", info.ServicePhone); //客服电话
  100. Dictionary<string, object> sales_info = new Dictionary<string, object>();
  101. info.SalesScenesType = "SALES_SCENES_STORE";
  102. sales_info.Add("sales_scenes_type", info.SalesScenesType.Split(',').ToList()); //经营场景类型
  103. Dictionary<string, object> biz_store_info = new Dictionary<string, object>();
  104. biz_store_info.Add("biz_store_name", info.BizStoreName); //门店名称
  105. PxcModels.OpenBankCityTable BizAddress = dbpxc.OpenBankCityTable.FirstOrDefault(m => m.CityName.EndsWith(info.BizAddressCode)) ?? new PxcModels.OpenBankCityTable();
  106. biz_store_info.Add("biz_address_code", BizAddress.Code); //门店省市编码
  107. biz_store_info.Add("biz_store_address", info.BizStoreAddress); //门店地址
  108. List<string> store_entrance_pic = new List<string>();
  109. store_entrance_pic.Add(GetMediaId("/" + info.StoreEntrancePic));
  110. biz_store_info.Add("store_entrance_pic", store_entrance_pic); //门店门头照片
  111. List<string> indoor_pic = new List<string>();
  112. indoor_pic.Add(GetMediaId("/" + info.IndoorPic));
  113. biz_store_info.Add("indoor_pic", indoor_pic); //店内环境照片
  114. biz_store_info.Add("biz_sub_appid", info.BizSubAppid); //线下场所对应的商家APPID
  115. sales_info.Add("biz_store_info", biz_store_info); //线下门店场景
  116. // Dictionary<string, object> mp_info = new Dictionary<string, object>();
  117. // mp_info.Add("mp_appid", info.MpAppid); //服务商公众号APPID
  118. // mp_info.Add("mp_sub_appid", info.MpSubAppid); //商家公众号APPID
  119. // mp_info.Add("mp_pics", GetMediaId("/" + info.MpPics)); //公众号页面截图
  120. // sales_info.Add("mp_info", mp_info); //公众号场景
  121. // Dictionary<string, object> mini_program_info = new Dictionary<string, object>();
  122. // mini_program_info.Add("mini_program_appid", info.MiniProgramAppid); //服务商小程序APPID
  123. // mini_program_info.Add("mini_program_sub_appid", info.MiniProgramSubAppid); //商家小程序APPID
  124. // mini_program_info.Add("mini_program_pics", GetMediaId("/" + info.MiniProgramPics)); //小程序截图
  125. // sales_info.Add("mini_program_info", mini_program_info); //小程序场景
  126. // Dictionary<string, object> app_info = new Dictionary<string, object>();
  127. // app_info.Add("app_appid", info.AppAppid); //服务商应用APPID
  128. // app_info.Add("app_sub_appid", info.AppSubAppid); //商家应用APPID
  129. // app_info.Add("app_pics", GetMediaId("/" + info.AppPics)); //商家应用APPID
  130. // sales_info.Add("app_info", app_info); //APP场景
  131. // Dictionary<string, object> web_info = new Dictionary<string, object>();
  132. // web_info.Add("domain", info.WebDomain); //互联网网站域名
  133. // web_info.Add("web_authorisation", GetMediaId("/" + info.WebAuthorisation)); //网站授权函
  134. // web_info.Add("web_appid", info.WebAppId); //互联网网站对应的商家APPID
  135. // sales_info.Add("web_info", web_info); //互联网网站场景
  136. // Dictionary<string, object> wework_info = new Dictionary<string, object>();
  137. // wework_info.Add("sub_corp_id", info.SubCorpId); //商家企业微信CorpID
  138. // wework_info.Add("wework_pics", GetMediaId("/" + info.WeworkPics)); //企业微信页面截图
  139. // sales_info.Add("wework_info", wework_info); //企业微信场景
  140. business_info.Add("sales_info", sales_info); //经营场景
  141. result.Add("business_info", business_info);
  142. //结算规则
  143. Dictionary<string, object> settlement_info = new Dictionary<string, object>();
  144. settlement_info.Add("settlement_id", info.SettlementId); //入驻结算规则ID
  145. string QualificationType = function.CheckNull(info.QualificationType);
  146. if (QualificationType.Contains(","))
  147. {
  148. QualificationType = QualificationType.Split(',')[0];
  149. }
  150. settlement_info.Add("qualification_type", QualificationType); //所属行业
  151. if (!string.IsNullOrEmpty(info.Qualifications))
  152. {
  153. List<string> Qualifications = new List<string>();
  154. Qualifications.Add(GetMediaId("/" + info.Qualifications));
  155. settlement_info.Add("qualifications", Qualifications); //特殊资质图片
  156. }
  157. settlement_info.Add("activities_id", info.ActivitiesId); //优惠费率活动ID
  158. settlement_info.Add("activities_rate", info.ActivitiesRate); //优惠费率活动值
  159. result.Add("settlement_info", settlement_info);
  160. //结算银行账户
  161. Dictionary<string, object> bank_account_info = new Dictionary<string, object>();
  162. bank_account_info.Add("bank_account_type", info.BankAccountType); //账户类型
  163. bank_account_info.Add("account_name", RSAEncrypt(info.AccountName)); //开户名称
  164. bank_account_info.Add("account_bank", info.AccountBank); //开户银行
  165. PxcModels.OpenBankCityTable BankAddress = dbpxc.OpenBankCityTable.FirstOrDefault(m => m.CityName.EndsWith(info.BankAddressCode)) ?? new PxcModels.OpenBankCityTable();
  166. bank_account_info.Add("bank_address_code", BankAddress.Code); //开户银行省市编码
  167. PxcModels.OpenBankTable BankBranch = dbpxc.OpenBankTable.FirstOrDefault(m => m.BankName == info.BankName) ?? new PxcModels.OpenBankTable();
  168. bank_account_info.Add("bank_branch_id", BankBranch.BankCode); //开户银行联行号
  169. bank_account_info.Add("bank_name", info.BankName); //开户银行全称(含支行)
  170. bank_account_info.Add("account_number", RSAEncrypt(info.AccountNumber)); //银行账号
  171. result.Add("bank_account_info", bank_account_info);
  172. dbpxc.Dispose();
  173. string req = Newtonsoft.Json.JsonConvert.SerializeObject(result);
  174. Dictionary<string, string> header = new Dictionary<string, string>();
  175. // 如何查看证书序列号?
  176. // 登陆商户平台【API安全】->【API证书】->【查看证书】,可查看商户API证书序列号。
  177. // 商户API证书和微信支付平台证书均可以使用第三方的证书解析工具,查看证书内容。或者使用openssl命令行工具查看证书序列号。
  178. // $ openssl x509 -in 1900009191_20180326_cert.pem -noout -serial
  179. // serial=1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C
  180. function.WriteLog(req, "服务商平台商户进件提交申请单API");
  181. string resp = postJson("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/", req, prikey, MchId, serialNo);
  182. function.WriteLog(resp + "\n\n", "服务商平台商户进件提交申请单API");
  183. return resp;
  184. }
  185. #endregion
  186. #region 服务商平台查询申请单状态API
  187. public Dictionary<string, object> QueryMerchant(string BusinessCode)
  188. {
  189. Dictionary<string, object> return_result = new Dictionary<string, object>();
  190. try
  191. {
  192. string result = postJson("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/" + BusinessCode, "", prikey, MchId, serialNo, "GET");
  193. //{\"applyment_id\":2000002247709762,\"applyment_state\":\"APPLYMENT_STATE_FINISHED\",\"applyment_state_msg\":\"商户入驻申请已完成\",\"audit_detail\":[],\"business_code\":\"0123456789\",\"sign_url\":\"https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQFv7zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyUXE1czkzb3JlUjIxZHpXbTF5Y2YAAgRjbe9hAwQAjScA\",\"sub_mchid\":\"1619775262\"}
  194. JsonData jsonObj = JsonMapper.ToObject(result);
  195. bool logFlag = true;
  196. if (jsonObj["applyment_state"].ToString() == "APPLYMENT_STATE_AUDITING" || jsonObj["applyment_state"].ToString() == "APPLYMENT_STATE_TO_BE_SIGNED" || jsonObj["applyment_state"].ToString() == "APPLYMENT_STATE_TO_BE_CONFIRMED")
  197. {
  198. string check = RedisDbconn.Instance.Get<string>("WeChatQueryMerchant:" + BusinessCode + ":" + jsonObj["applyment_state"].ToString());
  199. if(!string.IsNullOrEmpty(check))
  200. {
  201. logFlag = false;
  202. }
  203. else
  204. {
  205. RedisDbconn.Instance.Set("WeChatQueryMerchant:" + BusinessCode + ":" + jsonObj["applyment_state"].ToString(), result);
  206. RedisDbconn.Instance.SetExpire("WeChatQueryMerchant:" + BusinessCode + ":" + jsonObj["applyment_state"].ToString(), 7200);
  207. }
  208. }
  209. if(logFlag)
  210. {
  211. function.WriteLog(BusinessCode, "服务商平台商户进件提交申请单API");
  212. function.WriteLog(result + "\n\n", "服务商平台查询申请单状态API");
  213. }
  214. return_result.Add("applyment_id", jsonObj["applyment_id"].ToString()); //微信支付申请单号
  215. return_result.Add("applyment_state", jsonObj["applyment_state"].ToString()); //申请单状态
  216. return_result.Add("applyment_state_msg", jsonObj["applyment_state_msg"].ToString()); //申请状态描述
  217. if (jsonObj["applyment_state"].ToString() == "APPLYMENT_STATE_REJECTED")
  218. {
  219. List<Dictionary<string, string>> audit_detail = new List<Dictionary<string, string>>();
  220. JsonData auditObj = jsonObj["audit_detail"]; //驳回原因详情
  221. // for (int i = 0; i < auditObj.Count; i++)
  222. // {
  223. // Dictionary<string, string> row = new Dictionary<string, string>();
  224. // // row.Add("field", auditObj[i]["field"].ToString()); //字段名
  225. // // row.Add("field_name", auditObj[i]["field_name"].ToString()); //字段名称
  226. // row.Add("reject_reason", auditObj[i]["reject_reason"].ToString()); //驳回原因
  227. // audit_detail.Add(row);
  228. // }
  229. return_result.Add("reject_reason", auditObj[0]["reject_reason"].ToString());
  230. return_result.Add("audit_detail", audit_detail); //申请状态描述
  231. }
  232. if (result.Contains("\"sign_url\":"))
  233. {
  234. return_result.Add("sign_url", jsonObj["sign_url"].ToString()); //进件通过申请,待商家签约的地址
  235. }
  236. }
  237. catch (Exception ex)
  238. {
  239. function.WriteLog(DateTime.Now.ToString() + "\r\n" + ex.ToString(), "服务商平台查询申请单状态API异常");
  240. }
  241. return return_result;
  242. }
  243. #endregion
  244. #region 服务商平台申请退款API
  245. public Dictionary<string, string> Refund(string SubMchId, decimal RefundMoney, decimal TotalMoney, string OrderNo, string RefundNo)
  246. {
  247. Dictionary<string, string> return_result = new Dictionary<string, string>();
  248. int refund = (int)(RefundMoney * 100);
  249. int amount = (int)(TotalMoney * 100);
  250. //具体请求参数
  251. SortedList<string, object> par = new SortedList<string, object>();
  252. par.Add("sub_mchid", SubMchId); //子商户号
  253. par.Add("out_trade_no", OrderNo); //商户订单号
  254. par.Add("out_refund_no", RefundNo); //商户退款单号
  255. par.Add("reason", "活动返现"); //退款原因
  256. Dictionary<string, object> amountDic = new Dictionary<string, object>();
  257. amountDic.Add("refund", refund); //退款金额
  258. amountDic.Add("total", amount); //原订单金额
  259. amountDic.Add("currency", "CNY"); //退款币种
  260. par.Add("amount", amountDic); //金额信息
  261. string req = Newtonsoft.Json.JsonConvert.SerializeObject(par);
  262. try
  263. {
  264. function.WriteLog(req, "服务商平台申请退款API");
  265. string result = postJson("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds", req, prikey, MchId, serialNo);
  266. function.WriteLog(result + "\n\n", "服务商平台申请退款API");
  267. JsonData jsonObj = JsonMapper.ToObject(result);
  268. return_result.Add("refund_id", jsonObj["refund_id"].ToString()); //微信支付退款单号
  269. return_result.Add("channel", jsonObj["channel"].ToString()); //退款渠道
  270. return_result.Add("user_received_account", jsonObj["user_received_account"].ToString()); //退款入账账户
  271. return_result.Add("status", jsonObj["status"].ToString()); //退款状态
  272. return_result.Add("payer_total", jsonObj["amount"]["payer_total"].ToString()); //用户支付金额
  273. return_result.Add("payer_refund", jsonObj["amount"]["payer_refund"].ToString()); //用户退款金额
  274. return_result.Add("settlement_refund", jsonObj["amount"]["settlement_refund"].ToString()); //应结退款金额
  275. return_result.Add("settlement_total", jsonObj["amount"]["settlement_total"].ToString()); //应结订单金额
  276. return_result.Add("discount_refund", jsonObj["amount"]["discount_refund"].ToString()); //优惠退款金额
  277. }
  278. catch (Exception ex)
  279. {
  280. function.WriteLog(DateTime.Now.ToString() + "\r\n" + ex.ToString(), "服务商平台申请退款API异常");
  281. }
  282. return return_result;
  283. }
  284. #endregion
  285. #region 服务商平台请求分账API
  286. public string ProfitShare(string SubMchId, string TradeNo, string OrderNo, List<ReceiverList> Receivers) //有活动分0.62%,没活动不分账
  287. {
  288. Dictionary<string, object> result = new Dictionary<string, object>();
  289. result.Add("sub_mchid", SubMchId); //子商户号
  290. result.Add("appid", AppId); //应用ID
  291. result.Add("transaction_id", TradeNo); //微信订单号
  292. result.Add("out_order_no", OrderNo); //商户分账单号
  293. result.Add("receivers", Receivers); //分账接收方列表
  294. result.Add("unfreeze_unsplit", true); //是否解冻剩余未分资金
  295. string req = Newtonsoft.Json.JsonConvert.SerializeObject(result);
  296. function.WriteLog(req, "服务商平台请求分账API");
  297. string resp = postJson("https://api.mch.weixin.qq.com/v3/profitsharing/orders", req, prikey, MchId, serialNo);
  298. function.WriteLog(resp + "\n\n", "服务商平台请求分账API");
  299. return resp;
  300. //{"order_id":"30000104462023060249559178531","out_order_no":"2023060210583730173782834","receivers":[{"account":"1611167423","amount":3,"create_time":"2023-06-02T11:34:10+08:00","description":"服务费","detail_id":"36000104462023060269999025889","finish_time":"1970-01-01T08:00:00+08:00","result":"PENDING","type":"MERCHANT_ID"},{"account":"1645266943","amount":7,"create_time":"2023-06-02T11:34:10+08:00","description":"解冻给分账方","detail_id":"36000104462023060269999025890","finish_time":"1970-01-01T08:00:00+08:00","result":"PENDING","type":"MERCHANT_ID"}],"state":"PROCESSING","sub_mchid":"1645266943","transaction_id":"4200001833202306025522158377"}
  301. }
  302. #endregion
  303. #region 服务商平台添加分账接收方API
  304. public string AddReceive(string SubMchId, string TypeName, string Account, string Name, string Relation)
  305. {
  306. Dictionary<string, object> result = new Dictionary<string, object>();
  307. result.Add("sub_mchid", SubMchId); //子商户号
  308. result.Add("appid", AppId); //应用ID
  309. result.Add("type", TypeName); //分账接收方类型 MERCHANT_ID:商户ID PERSONAL_OPENID:个人openid(由父商户APPID转换得到) PERSONAL_SUB_OPENID:个人sub_openid(由子商户APPID转换得到)
  310. result.Add("account", Account); //分账接收方账号
  311. if(!string.IsNullOrEmpty(Name))
  312. {
  313. result.Add("name", RSAEncrypt(Name)); //分账个人接收方姓名
  314. }
  315. result.Add("relation_type", Relation); //与分账方的关系类型 STORE:门店 STAFF:员工 STORE_OWNER:店主 PARTNER:合作伙伴 HEADQUARTER:总部 BRAND:品牌方 DISTRIBUTOR:分销商 USER:用户 SUPPLIER: 供应商 CUSTOM:自定义
  316. string req = Newtonsoft.Json.JsonConvert.SerializeObject(result);
  317. function.WriteLog(req, "服务商平台添加分账接收方API");
  318. string resp = postJson("https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add", req, prikey, MchId, serialNo);
  319. function.WriteLog(resp + "\n\n", "服务商平台添加分账接收方API");
  320. return resp;
  321. //{"account":"1611167423","name":"x+8m5jM4x2jSZLDOpN46Js9JyeNgUXkFSmGjCBN45QcSYUNPN86TOheRF64WlwDeO45E/walWPeZSksNN/LfJiB2fZRVun1CJuJ7zxePma8L7iDbKS92i+d38rvOmNTjLhKX64O8oJR4HMtFw1HryxJlUFgM5/ickJ79WGrX3GaVArWisgEzwOyxGBfGSL3ofJhi5TdTnrxHMzqYWv43DRvI9Ja5eMgFbgrGcn+JiJVKQcYj1L8tZ6xQeQCXkY6ubyDiV9wQps2nQA1Oh+UdFcS13Ex4FUSYLCczGFAl4EV8TBJQleZeC1p4ZJquJjje0uMjI2xB6S41yUBoN86gkA==","relation_type":"PARTNER","sub_mchid":"1645266943","type":"MERCHANT_ID"}
  322. }
  323. #endregion
  324. #region 敏感信息加密
  325. string pubkey = "MIID3DCCAsSgAwIBAgIUU0FlQhvigU3sCPjQC9q5lEsTZGwwDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsTFFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3QgQ0EwHhcNMjEwNjMwMDMwOTA5WhcNMjYwNjI5MDMwOTA5WjBuMRgwFgYDVQQDDA9UZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRlbnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpoZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC64SIwN1afgge2pjFeNZAlKz5ZdJw8W/hisaIJnkzbE/1oG5n+SgtbMLfBLBFp0zMyT5SP5ve5aJAvUjwnRwQoDG+4z3jE8vLyjFLxoy5sWGex8+FBC5hs81oAUHuazigi5NcSH9Ji/8Lq3zFh8Az4+ZchQ14SFhSGTifSZfdTX5wYDEv3DKDYgQvvAgDgjBF1Uj91NpVD7ruQjAJ7MwZvUX7rnYFnQggFQwIBWazNXaGa7OpO6Xbf+uNtbl436My7WQX+ffjmtYmcgND60mgn/rFQMg5r+Q+J8mDgIaAzCmr3xW31OIvAD1iYV0ksTyQntD5pOzCYdHZPDOZCqYlHAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DBlBgNVHR8EXjBcMFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5jb20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJKoZIhvcNAQELBQADggEBAEgXlFe4MpFUUnj6gJt5KoPpzLTNOXDHsglUrAobq/HlmDBOF8yCUFvtbRaJxUSQVFfo/2zag8J57YNMo2If1HkD1LMEGAcPGNS+aho9SgCFbLG+c82nNICl8g8oe1HNS7IpRjacSfrKIYt5VL5lHHC5ZJaKq0Ez9c7bfPGC6CaA8WDnApFZut5qhYNMBUYZZjAMVIZ2G/kIsxbo1oUpVEnQq5YQxPUDWdYKxdTvo7uXXpWv6qo761Hid24R7mdrGEuHZNBfxYCt1wJQNlcGVgCjk8PPo73mO0zYRwV0x7pNkFFbiP7aetf6RB82YUbuNkvql83Ox+e22ByMi8lOUZo=";
  326. string prikey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD7f7VmtjtLJaY+oJkMgDN9dDE082ZOgquHorPrtJNEdf7VjRFLMoKOoiQsT4OgVq3lNN2KKndd5ahsegSq7Ng7aYJq7IDH584RWEGbekEn0M10aXThdk9dPSCMXBPFR6y4gMvV4R6pVbPBxoSFMI6ty+EqfZLP9Cxvxx7msIKEB46r2R6gYL7j061ytoURavUxGAZxMXnCAEckbrssiFwhLTmX6TtC8MVPyUfLPUh07ArRZxK2F0hpX35Te0Jda7xkulbWtz4EB8KPXr0bJGO/oUo+Prqpdjy0+zKMfMvBP6NG1Hnh3z7yBhlYIe4vymFQU1Wy0OtrEbrU1OdJiq5XAgMBAAECggEAJ7biAWczc8ltOCSKJpQ0DVKE1rS86hbuWUvTF5x7SOtNI5HUMki2nylxhuHDilVFvpUbis7r7JLwhri59ffSqYcexh20tFyhiNGkApZKpavuVby1bUkOfU3CzwdXQ+bumnAUQCD9g4X6DdZ4ZMPnNACm/Nu5jEcloT0TXyBua8SpmlFMYgo/x8XOTT4dEeubwNsfEpmSeWcQRJLJI5ujGgmmZLiAs7F3VlCJFNtQa1KT3a1EDhD5jajeQAzsPpQ48i/joOqE71RlVr77z4Ethr8o1Cc16K9Md2AtVesMZ6eZNqKkQ/qC3vnxk5Sy25tuVmOCXa81ErIf9Tf/JwhymQKBgQD/TXnYxEzYJCqa7/Ck6QqBHQf4ak1OVz7/NY7bmxYT5ez4eJKzrh+xQYWBq/UmKeEiH5QI6T3MVo7L20tbeuYLQwYpgb+BpOKHFne2ODOgDYreNREB06Ww2qAQYCqwyBnTqt7UthDmcXhcPojwxNLSJAOCQhld9TOOmK1db33PIwKBgQD8L5KiN9XRFCvUH6rhUcs/8lmGR2N8zSTts+BNCwIPGQWE0UvbjroyNxVbq6cXXlZZ6H07vEAgaIrLIkwLgen2IwsuOiyaEMvRP9oGwC+LnpP+9sxbW5w5do1hfyeGiSPPgSYIWZMOOwX+qfqXDMM3whJP1hk4El87kC+kl2URPQKBgQC4kTc8ebWgMI1Ik5wibIN2khlYAum+cB6P/BU6OCU+32NwwiI0mfY5fEfbVgiVvYMP20p3Ir/VgOIdCH2zNecnTx4eVbmwLRbDWH/VEU5pMEEroauY6XyInZVIIW3bfd4GAyqzFqijIHdGIMN5ZKGQ4A5VVhnmrkTGmyQCP37xhwKBgQCaLW+Fc4NqTt240x+Cn6IWV1E6UfzV+QmBtb4E/BznJBIGsHqjIbBeMOUC5X/DPq6mSwf9dEr0RVXNeQtq4aMrEyOrwG8yReCF7naaN/cZcfrjHvmzRN/PFda766Q7kS5ul3OsGS9ZvOYeWsdUf73+Ic1WN/u1Fy5X2P/3rYNhtQKBgAIioAq8psFdy/itpJryaWdmDM6fM6ctp5P6VBWOVp2JGkOzHboiXFToJJBtmi7Rdp8AN1aYBbtVX1o0+RmfEiHqXn84GpSNT6tGirHvARi5ab3jvn+bkUWjZYOGd96hus8GirA+wQb/V92Ms3jS5VUS1HBfHaTGT5G2Slrr8YZu"; //私钥
  327. public string RSAEncrypt(string text)
  328. {
  329. byte[] publicKey = Convert.FromBase64String(pubkey);
  330. // var rsa = RSA.Create();
  331. // rsa.ImportRSAPublicKey(publicKey, out _);
  332. // var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.OaepSHA1);
  333. // return Convert.ToBase64String(buff);
  334. using (var x509 = new X509Certificate2(publicKey))
  335. {
  336. using (var rsa = x509.GetRSAPublicKey())
  337. {
  338. var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.OaepSHA1);
  339. return Convert.ToBase64String(buff);
  340. }
  341. }
  342. }
  343. // public string RSADecrypt(string text)
  344. // {
  345. // // byte[] key = Encoding.UTF8.GetBytes(AppConfig.WeChatParam.AesGemKey);
  346. // // byte[] nonce = Encoding.UTF8.GetBytes("1234567890ab");
  347. // // byte[] ciphertext = Convert.FromBase64String(text);
  348. // // AesGcm aes = new AesGcm(key);
  349. // // byte[] plaintext = new byte[ciphertext.Length];
  350. // // aes.Decrypt(nonce, ciphertext, plaintext, null);
  351. // // return Encoding.UTF8.GetString(plaintext);
  352. // }
  353. public string AesGcmDecrypt(string associatedData, string nonce, string ciphertext)
  354. {
  355. // GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
  356. // AeadParameters aeadParameters = new AeadParameters(
  357. // new KeyParameter(Encoding.UTF8.GetBytes(AppConfig.WeChatParam.AesGemKey)),
  358. // 128,
  359. // Encoding.UTF8.GetBytes(nonce),
  360. // Encoding.UTF8.GetBytes(associatedData));
  361. // gcmBlockCipher.Init(false, aeadParameters);
  362. // byte[] data = Convert.FromBase64String(ciphertext);
  363. // byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];
  364. // int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
  365. // gcmBlockCipher.DoFinal(plaintext, length);
  366. // return Encoding.UTF8.GetString(plaintext);
  367. byte[] key = Encoding.UTF8.GetBytes(AppConfig.WeChatParam.AesGemKey); // 256-bit key
  368. byte[] nonceByte = Encoding.UTF8.GetBytes(nonce); // 96-bit nonce
  369. byte[] cipherByte = Convert.FromBase64String(ciphertext);
  370. byte[] associatedByte = Encoding.UTF8.GetBytes(associatedData);
  371. GcmBlockCipher cipher = new GcmBlockCipher(new AesEngine());
  372. AeadParameters parameters = new AeadParameters(new KeyParameter(key), 128, nonceByte, associatedByte);
  373. cipher.Init(false, parameters);
  374. byte[] plaintext = new byte[cipher.GetOutputSize(cipherByte.Length)];
  375. int len = cipher.ProcessBytes(cipherByte, 0, cipherByte.Length, plaintext, 0);
  376. cipher.DoFinal(plaintext, len);
  377. return Encoding.UTF8.GetString(plaintext);
  378. }
  379. public string postJson(string url, string postData, string privateKey, string merchantId, string serialNo, string method = "POST")
  380. {
  381. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
  382. request.Method = method;
  383. request.ContentType = "application/json;charset=UTF-8";
  384. request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36";
  385. request.Accept = "application/json";
  386. string Authorization = GetAuthorization(url, method, postData, privateKey, merchantId, "62B64854B768D344249FDB9E6EF4A1232C000E84");
  387. request.Headers.Add("Authorization", Authorization);
  388. request.Headers.Add("Wechatpay-Serial", serialNo);
  389. if (!string.IsNullOrEmpty(postData))
  390. {
  391. byte[] paramJsonBytes;
  392. paramJsonBytes = System.Text.Encoding.UTF8.GetBytes(postData);
  393. request.ContentLength = paramJsonBytes.Length;
  394. Stream writer;
  395. try
  396. {
  397. writer = request.GetRequestStream();
  398. }
  399. catch (Exception)
  400. {
  401. writer = null;
  402. Console.Write("连接服务器失败!");
  403. }
  404. writer.Write(paramJsonBytes, 0, paramJsonBytes.Length);
  405. writer.Close();
  406. }
  407. HttpWebResponse response;
  408. try
  409. {
  410. response = (HttpWebResponse)request.GetResponse();
  411. }
  412. catch (WebException ex)
  413. {
  414. response = ex.Response as HttpWebResponse;
  415. }
  416. Stream resStream = response.GetResponseStream();
  417. StreamReader reader = new StreamReader(resStream);
  418. string text = reader.ReadToEnd();
  419. return text;
  420. //{\"code\":\"PARAM_ERROR\",\"message\":\"请确认待处理的消息是否为加密后的密文\"}
  421. //{\"applyment_id\":2000002247709762}
  422. }
  423. #endregion
  424. #region 图片上传
  425. public string GetMediaId(string imgPath)
  426. {
  427. if (string.IsNullOrEmpty(imgPath))
  428. {
  429. return "";
  430. }
  431. if (imgPath.Contains(","))
  432. {
  433. imgPath = imgPath.Split(',')[0];
  434. }
  435. string key = "wechatpic:" + function.MD5_16(imgPath);
  436. string media_id = RedisDbconn.Instance.Get<string>(key);
  437. if (!string.IsNullOrEmpty(media_id))
  438. {
  439. return media_id;
  440. }
  441. string filePath = function.getPath(imgPath);
  442. var filename = Path.GetFileName(filePath);
  443. FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
  444. Byte[] imgBytesIn = new Byte[fs.Length];
  445. fs.Read(imgBytesIn, 0, imgBytesIn.Length);
  446. fs.Close();
  447. byte[] hash = SHA256Managed.Create().ComputeHash(imgBytesIn);
  448. StringBuilder builder = new StringBuilder();
  449. for (int i = 0; i < hash.Length; i++)
  450. {
  451. builder.Append(hash[i].ToString("x2"));
  452. }
  453. var sha256 = builder.ToString();
  454. string metaStr = "{\"filename\":\""+ filename + "\",\"sha256\":\"" + sha256 + "\"}";
  455. media_id = UploadImgApi(metaStr, imgBytesIn, filename);
  456. RedisDbconn.Instance.Set(key, media_id);
  457. return media_id;
  458. }
  459. public string UploadImgApi(string metaStr, Byte[] imgBytesIn,string filename)
  460. {
  461. string url = "https://api.mch.weixin.qq.com/v3/merchant/media/upload";
  462. string merchantId = "1611167423"; //商户号
  463. string serialNo = "62B64854B768D344249FDB9E6EF4A1232C000E84"; //证书编号
  464. string privateKey = prikey;
  465. #region 定义请求体中的内容 并转成二进制
  466. string boundary = "lc199aecd61b4653ef";
  467. string Enter = "\r\n";
  468. string campaignIDStr1
  469. = "--" + boundary
  470. + Enter
  471. + "Content-Disposition: form-data; name=\"meta\";"
  472. + Enter
  473. + "Content-Type:application/json;"
  474. + Enter
  475. + Enter
  476. + metaStr
  477. + Enter
  478. + "--" + boundary
  479. + Enter
  480. + "Content-Disposition:form-data;name=\"file\";filename=\""+ filename + "\";"
  481. + Enter
  482. + "Content-Type:image/jpeg"
  483. + Enter
  484. + Enter;
  485. byte[] byteData2
  486. = imgBytesIn;
  487. string campaignIDStr3
  488. = Enter
  489. + "--" + boundary
  490. + Enter;
  491. var byteData1 = System.Text.Encoding.UTF8.GetBytes(campaignIDStr1);
  492. var byteData3 = System.Text.Encoding.UTF8.GetBytes(campaignIDStr3);
  493. #endregion
  494. string transactionsResponse = UploadImg_postJson(url, byteData1, byteData2, byteData3, metaStr, privateKey, merchantId, serialNo, boundary, "POST");
  495. var result=JsonMapper.ToObject(transactionsResponse);
  496. return result["media_id"].ToString();
  497. }
  498. public string UploadImg_postJson(string url, byte[] b1, byte[] b2, byte[] b3, string metaStr, string privateKey, string merchantId, string serialNo, string boundary, string method = "POST")
  499. {
  500. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
  501. request.Method = method;
  502. //request.ContentType = "application/json;charset=UTF-8";
  503. request.ContentType = "multipart/form-data;boundary=" + boundary;
  504. request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36";
  505. request.Accept = "application/json";
  506. string Authorization = GetAuthorization(url, method, metaStr, privateKey, merchantId, serialNo);
  507. request.Headers.Add("Authorization", Authorization);
  508. Stream writer;
  509. try
  510. {
  511. writer = request.GetRequestStream();
  512. }
  513. catch (Exception)
  514. {
  515. writer = null;
  516. }
  517. writer.Write(b1, 0, b1.Length);
  518. writer.Write(b2, 0, b2.Length);
  519. writer.Write(b3, 0, b3.Length);
  520. writer.Close();
  521. HttpWebResponse response;
  522. try
  523. {
  524. response = (HttpWebResponse)request.GetResponse();
  525. }
  526. catch (WebException ex)
  527. {
  528. response = ex.Response as HttpWebResponse;
  529. }
  530. Stream resStream = response.GetResponseStream();
  531. StreamReader reader = new StreamReader(resStream);
  532. string text = reader.ReadToEnd();
  533. return text;
  534. }
  535. protected string GetAuthorization(string url, string method, string jsonParame, string privateKey, string merchantId, string serialNo)
  536. {
  537. var uri = new Uri(url);
  538. string urlPath = uri.PathAndQuery;
  539. string nonce = Guid.NewGuid().ToString();
  540. var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
  541. //数据签名 HTTP请求方法\n接口地址的url\n请求时间戳\n请求随机串\n请求报文主体\n
  542. method = string.IsNullOrEmpty(method) ? "" : method;
  543. string message = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", method, urlPath, timestamp, nonce, jsonParame);
  544. string signTxt = Sign(message, privateKey);
  545. //Authorization和格式
  546. string authorzationTxt = string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\"{0}\",nonce_str=\"{1}\",timestamp=\"{2}\",serial_no=\"{3}\",signature=\"{4}\"",
  547. merchantId,
  548. nonce,
  549. timestamp,
  550. serialNo,
  551. signTxt
  552. );
  553. return authorzationTxt;
  554. }
  555. protected string Sign(string message, string privateKey)
  556. {
  557. byte[] keyData = Convert.FromBase64String(privateKey);
  558. byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
  559. var rsa = RSA.Create();
  560. rsa.ImportPkcs8PrivateKey(keyData, out _);
  561. return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
  562. // using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
  563. // using (RSACng rsa = new RSACng(cngKey))
  564. // {
  565. // return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
  566. // }
  567. }
  568. #endregion
  569. #region 获取文件sha256
  570. public string GetSha256(FileStream stream)
  571. {
  572. using (SHA256 mySHA256 = SHA256.Create())
  573. {
  574. byte[] hashValue = mySHA256.ComputeHash(stream);
  575. return Encoding.UTF8.GetString(hashValue);
  576. }
  577. }
  578. #endregion
  579. #region 判断长期
  580. public string CheckForever(DateTime? time)
  581. {
  582. if (time == null)
  583. {
  584. return "";
  585. }
  586. if (time.Value.Year >= 2050)
  587. {
  588. return "长期";
  589. }
  590. return time.Value.ToString("yyyy-MM-dd");
  591. }
  592. #endregion
  593. }
  594. }