{"id":128,"date":"2023-10-24T11:30:07","date_gmt":"2023-10-24T03:30:07","guid":{"rendered":"https:\/\/www.liaoxinghui.com\/?p=128"},"modified":"2023-10-24T11:30:08","modified_gmt":"2023-10-24T03:30:08","slug":"%e5%af%b9%e6%8e%a5%e5%be%ae%e4%bf%a1%e6%94%af%e4%bb%98%e6%8e%a5%e5%8f%a3","status":"publish","type":"post","link":"https:\/\/www.liaoxinghui.com\/?p=128","title":{"rendered":"\u5bf9\u63a5\u5fae\u4fe1\u652f\u4ed8\u63a5\u53e3"},"content":{"rendered":"<p class=\"wp-block-paragraph\">\u9879\u76ee\u662fPyhton+fastapi\u6846\u67b6\uff0c\u5728\u5bf9\u63a5\u5fae\u4fe1\u652f\u4ed8\u63a5\u53e3\u65f6\u771f\u662f\u4e00\u8a00\u96be\u5c3d\uff0c\u817e\u8baf\u7684\u5927\u4f6c\u4eec\u77e5\u9053\u81ea\u5df1\u7684\u63a5\u53e3\u6587\u6863\u5f88\u4e0d\u597d\u9605\u8bfb\u5417\uff1f <\/p>\n<p class=\"wp-block-paragraph\">1.\u56de\u8c03\u51fd\u6570\u6ca1\u6709\u8bf4\u660e\u8bf7\u6c42\u65b9\u5f0f\uff0c\u53c2\u6570\u53d1\u9001\u65b9\u5f0f\uff0c\u53ea\u80fd\u81ea\u5df1\u6765\u8bd5\u54af\uff0c\u53c8\u4e0d\u6562\u56fa\u5b9ajson\u6216\u8005URL-CODE\uff0c\u5168\u90e8\u63a5\u6536\u5457\uff0c\u6700\u540e\u8bd5\u51fa\u6765\u56de\u8c03\u51fd\u6570\u662fPOST\uff0c\u53c2\u6570\u662fjson\uff0c\u4e0b\u9762\u662f\u6211\u9879\u76ee\u7684\u56de\u8c03\u63a5\u53e3\u63a5\u6536\u7684\u53c2\u6570\uff1a<\/p>\n<pre class=\"wp-block-code\"><code>{\n&#039;id&#039;: &#039;2799da17-e092-539e-bf65-6eccc4c21a3d&#039;, \n&#039;create_time&#039;: &#039;2023-09-29T19:33:49+08:00&#039;, \n&#039;resource_type&#039;: &#039;encrypt-resource&#039;, \n&#039;event_type&#039;: &#039;TRANSACTION.SUCCESS&#039;, \n&#039;summary&#039;: &#039;\u652f\u4ed8\u6210\u529f&#039;, \n&#039;resource&#039;: \n{\n&#039;original_type&#039;: &#039;transaction&#039;, \n&#039;algorithm&#039;: &#039;AEAD_AES_256_GCM&#039;, \n&#039;ciphertext&#039;: &#039;X5HRECMZnfX79+Md7mcb6+z\/Hts3HmdluTOllo7rw\/ga9NLQ2s2b\/b6LeuZOsSPQgMKuAMUM88HT2mf2ZRzlVE44siKd8Dd4B8C5eI9DTUmqv7YlJC6m0TaRvwU3jN2xdBBhIErxb6kCdSvTuSohh8OY+d+T1wlJuRVbiZ519C+Rs6BnTKRcb2Ma\/Rc1Axtz04hxK2VBLBHhlBXxeR7UhO0W51noP0eTYLGzPEPIbJrAFd+WbLM5cwec2\/2stBpDUFuy5wAm3iSa7iPxy8b+y7JV7i2QeEhpm6a3+06YUxWzcrmr\/fO5nbzHUOu2EOzMD\/V7E14MWfdZpsAP7qKSWY6BeNKenQGO1+x4yLDV18z\/7XVQOj0Wx5aDVIPr0cZ35eKkDPpbJhmuWxGC8t5JW6\/SXClg+REWUmdNqyKWtLfjt3fXzTZAUjuwZMVKWtciy6SnuuSNeQC1Zxz8taGrLXsgfmSbjh1+BodnxtDbkShdaf5ysIDApkjdN8tUsSOYxE8lLj3fJ2xdSKLvWNPcaUhMxqYqxB43tnkKFdxFFXNMDwefK3lx3INEg==&#039;, \n&#039;associated_data&#039;: &#039;transaction&#039;, \n&#039;nonce&#039;: &#039;ef9nN1ofb93a&#039;\n}\n}<\/code><\/pre>\n<p class=\"wp-block-paragraph\">\u90e8\u5206\u654f\u611f\u7684\u5185\u5bb9\u6211\u6539\u4e86\u4e00\u4e0b\uff0c\u5927\u6982\u5c31\u662f\u8fd9\u4e2a\u610f\u601d\uff0c\u91cd\u8981\u7684\u4fe1\u606f\u653e\u5230ciphertext\u4e2d\uff0c\u88ab\u52a0\u5bc6\u4e86\uff0c\u4f60\u901a\u8fc7\u81ea\u5df1\u8bbe\u7f6e\u7684\u79d8\u94a5\u89e3\u5bc6\u5c31\u53ef\u4ee5\u4e86\uff0c\u4e0b\u9762\u662f\u4ee3\u7801\u793a\u4f8b<\/p>\n<pre class=\"wp-block-code\"><code># \u89e3\u7801\u5fae\u4fe1\u652f\u4ed8\u56de\u8c03\u6570\u636e\ndef decode_wechat_pay_call_back(self, nonce, ciphertext, associated_data):\n    # \u83b7\u53d6\u5fae\u4fe1\u652f\u4ed8API V3\u5bc6\u94a5\n    key = get_conf.get_wxpay_apiv3_key\n    # \u5c06\u5bc6\u94a5\u8f6c\u6362\u4e3a\u5b57\u8282\u7c7b\u578b\n    key_bytes = str.encode(key)\n    # \u5c06nonce\u8f6c\u6362\u4e3a\u5b57\u8282\u7c7b\u578b\n    nonce_bytes = str.encode(nonce)\n    ad_bytes = str.encode(associated_data)\n\n    # \u89e3\u7801\u5bc6\u6587\n    data = base64.b64decode(ciphertext)\n\n    # \u4f7f\u7528AES-GCM\u7b97\u6cd5\u89e3\u5bc6\u6570\u636e\n    aesgcm = AESGCM(key_bytes)\n    res = aesgcm.decrypt(nonce_bytes, data, ad_bytes)\n\n    # \u5c06\u89e3\u5bc6\u540e\u7684\u6570\u636e\u8f6c\u6362\u4e3a\u5b57\u7b26\u4e32\n    str_data = res.decode(&quot;utf-8&quot;)\n\n    # \u5c06\u5b57\u7b26\u4e32\u6570\u636e\u8f6c\u6362\u4e3aJSON\u5bf9\u8c61\n    return json.loads(str_data)<\/code><\/pre>\n<p class=\"wp-block-paragraph\">\u7136\u540e\u6765\u8bf4\u8bf4\u652f\u4ed8\u90e8\u5206\uff1a<\/p>\n<p class=\"wp-block-paragraph\">1.\u6309\u7167\u6587\u6863\u8981\u6c42\u751f\u6210\u968f\u673a\u5b57\u7b26\u4e32<\/p>\n<pre class=\"wp-block-code\"><code>random_str = &#039;&#039;.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))<\/code><\/pre>\n<p class=\"wp-block-paragraph\">2.\u5b8c\u6574\u7684\u8c03\u7528\uff0c\u5176\u4e2d\u5982\u679c\u7f3a\u5c11\u4e00\u4e9b\u5c0f\u7684\u65b9\u6cd5\uff0c\u81ea\u5df1\u5b9e\u73b0\u4e00\u4e0b\u5c31\u53ef\u4ee5\u4e86\uff0c\u6211\u662f\u4ece\u914d\u7f6e\u6587\u4ef6\u8bfb\u53d6\u7684\u914d\u7f6e\uff0c\u4f60\u4eec\u8981\u5199\u6b7b\u4e5f\u65e0\u6240\u8c13<\/p>\n<pre class=\"wp-block-code\"><code>    # -*- coding: utf-8 -*-\n&quot;&quot;&quot;\nAuthor : Taering\nProject : fastapi_openaiv2\nDescription \uff1a\u5fae\u4fe1\u652f\u4ed8\u5de5\u5177\u7c7b\n2023\/9\/28\n&quot;&quot;&quot;\nimport hashlib\nimport hmac\nimport json\nimport random\nimport string\nimport time\nfrom base64 import b64encode\nfrom datetime import datetime, timedelta\nfrom pathlib import Path\nfrom urllib.parse import urlparse\nimport qrcode\nimport requests\nfrom Crypto.Hash import SHA256\nfrom Crypto.PublicKey import RSA\nfrom Crypto.Signature import pkcs1_15\nfrom util.config.getconfig import Conf\nfrom util.global_logger_config import api_logger\n\nBASE_DIR = Path(__file__).resolve().parent.parent\n\n# \u5b9e\u4f8b\u5316\u8bfb\u53d6\u914d\u7f6e\u6587\u4ef6\nget_conf = Conf()\n\napi_logger = api_logger()\n\nclass WeChatPay(object):\n    &quot;&quot;&quot;\n    \u5fae\u4fe1\u652f\u4ed8\u5de5\u5177\u7c7b\n    &quot;&quot;&quot;\n\n    def __init__(self):\n        # \u4ece\u914d\u7f6e\u6587\u4ef6\u4e2d\u83b7\u53d6\u5fae\u4fe1\u652f\u4ed8\u7684\u76f8\u5173\u53c2\u6570\n        self.appid = get_conf.get_wxpay_appid  # APPID\n        self.mchid = get_conf.get_wxpay_mchid  # \u5546\u6237\u53f7\n        self.payment_url = get_conf.get_wxpay_payment_url  # Native\u652f\u4ed8\u4e0b\u5355\u63a5\u53e3\n        self.refund_url = get_conf.get_wxpay_refund_url  # \u9000\u6b3e\u63a5\u53e3\n        self.notify_url = get_conf.get_wxpay_notify_url  # \u56de\u8c03url\n        self.serial_no = get_conf.get_wxpay_serial_no  # \u5546\u6237\u8bc1\u4e66\u5e8f\u5217\u53f7\n        self.apiclient_key = str(BASE_DIR) + r&#039;\\util\\keyfiles\\wechatkeyfile\\apiclient_key.pem&#039;  # \u672c\u5730\u8bc1\u4e66\u8def\u5f84\n\n    # \u751f\u6210\u7b7e\u540d\n    def get_sign(self, sign_str):\n        # \u4ece\u672c\u5730\u8bfb\u53d6\u79c1\u94a5\n        rsa_key = RSA.importKey(open(self.apiclient_key).read())\n        # \u4f7f\u7528PKCS1_v1_5\u8fdb\u884c\u7b7e\u540d\n        signer = pkcs1_15.new(rsa_key)\n        # \u4f7f\u7528SHA256\u8fdb\u884c\u54c8\u5e0c\n        digest = SHA256.new(sign_str.encode(&#039;utf8&#039;))\n        # \u751f\u6210\u7b7e\u540d\n        sign = b64encode(signer.sign(digest)).decode(&#039;utf-8&#039;)\n        return sign\n\n    def request(self, url: str, method: str, data: dict = None):\n        # \u5c06data\u8f6c\u4e3ajson\u683c\u5f0f\n        data = json.dumps(data) if data else &#039;&#039;\n        # \u751f\u6210\u968f\u673a\u5b57\u7b26\u4e32nonce_str\n        random_str = &#039;&#039;.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))\n        # \u83b7\u53d6\u5f53\u524d\u65f6\u95f4\u6233\n        timestamp = str(int(time.time()))\n        # \u62fc\u63a5\u5f85\u7b7e\u540d\u5b57\u7b26\u4e32\n        sign_str = &#039;\\n&#039;.join([\n            method.upper(),  # HTTP\u8bf7\u6c42\u65b9\u6cd5\n            url.split(urlparse(url).netloc)[-1],  # path+args\n            timestamp,  # \u65f6\u95f4\u6233\n            random_str,  # \u8bf7\u6c42\u968f\u673a\u4e32\n            data, &#039;&#039;  # \u8bf7\u6c42\u62a5\u6587\u4e3b\u4f53\n        ])  # \u7ed3\u5c3e\u7a7a\u7a9c\u4ec5\u7528\u4e8e\u8ba9\u540e\u9762\u591a\u4e00\u4e2a\\n\n        # \u751f\u6210\u7b7e\u540d\n        sign = self.get_sign(sign_str)\n\n        # \u8bbe\u7f6e\u8bf7\u6c42\u5934\n        headers = {\n            &#039;Content-Type&#039;: &#039;application\/json; charset=UTF-8&#039;,\n            &#039;Authorization&#039;: f&#039;WECHATPAY2-SHA256-RSA2048 mchid=&quot;{self.mchid}&quot;,nonce_str=&quot;{random_str}&quot;,signature=&quot;{sign}&quot;,timestamp=&quot;{timestamp}&quot;,serial_no=&quot;{self.serial_no}&quot;&#039;\n        }\n        # \u53d1\u8d77\u8bf7\u6c42\n        response = requests.request(url=url, method=method, data=data\n<\/code><\/pre>","protected":false},"excerpt":{"rendered":"<p>\u9879\u76ee\u662fPyhton+fastapi\u6846\u67b6\uff0c\u5728\u5bf9\u63a5\u5fae\u4fe1\u652f\u4ed8\u63a5\u53e3\u65f6\u771f\u662f\u4e00\u8a00\u96be\u5c3d\uff0c\u817e\u8baf\u7684\u5927\u4f6c\u4eec\u77e5\u9053\u81ea\u5df1\u7684\u63a5\u53e3\u6587\u6863\u5f88\u4e0d\u597d\u9605\u8bfb\u5417\uff1f 1.\u56de\u8c03\u51fd\u6570\u6ca1\u6709\u8bf4 &hellip;<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[20,18,21],"class_list":["post-128","post","type-post","status-publish","format-standard","hentry","category-python","tag-fastapi","tag-python","tag-21"],"views":562,"_links":{"self":[{"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=\/wp\/v2\/posts\/128","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=128"}],"version-history":[{"count":1,"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=\/wp\/v2\/posts\/128\/revisions"}],"predecessor-version":[{"id":129,"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=\/wp\/v2\/posts\/128\/revisions\/129"}],"wp:attachment":[{"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=128"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=128"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.liaoxinghui.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}