73 lines
3.2 KiB
Python
73 lines
3.2 KiB
Python
import hashlib
|
|
import hmac
|
|
import json
|
|
import os
|
|
from types import SimpleNamespace
|
|
from unittest.mock import Mock, patch
|
|
|
|
from services import wechat_virtual_payment as virtual
|
|
|
|
|
|
def test_build_payment_params_signs_the_exact_compact_payload():
|
|
order = SimpleNamespace(order_no="AV202609080001", plan_id="plan-1", price_cents=1000)
|
|
plan = SimpleNamespace(id="plan-1", virtual_product_id="points_plan_1")
|
|
env = {
|
|
"WECHAT_VIRTUAL_ENV": "sandbox",
|
|
"WECHAT_VIRTUAL_SANDBOX_APP_KEY": "sandbox-key",
|
|
"WECHAT_VIRTUAL_OFFER_ID": "offer-1",
|
|
}
|
|
with patch.dict(os.environ, env, clear=False):
|
|
result = virtual.build_payment_params(order=order, plan=plan, session_key="session-key")
|
|
|
|
sign_data = result["signData"]
|
|
assert sign_data == json.dumps({
|
|
"offerId": "offer-1",
|
|
"buyQuantity": 1,
|
|
"env": 1,
|
|
"currencyType": "CNY",
|
|
"productId": "points_plan_1",
|
|
"goodsPrice": 1000,
|
|
"outTradeNo": "AV202609080001",
|
|
"attach": '{"orderNo":"AV202609080001","planId":"plan-1"}',
|
|
}, ensure_ascii=False, separators=(",", ":"))
|
|
assert result["paySig"] == hmac.new(
|
|
b"sandbox-key", f"requestVirtualPayment&{sign_data}".encode(), hashlib.sha256
|
|
).hexdigest()
|
|
assert result["signature"] == hmac.new(
|
|
b"session-key", sign_data.encode(), hashlib.sha256
|
|
).hexdigest()
|
|
|
|
|
|
def test_callback_signature_and_xml_body_are_supported():
|
|
with patch.dict(os.environ, {"WECHAT_VIRTUAL_CALLBACK_TOKEN": "callback-token"}):
|
|
signature = hashlib.sha1("".join(sorted(["callback-token", "100", "nonce"])).encode()).hexdigest()
|
|
assert virtual.verify_callback_signature(signature, "100", "nonce")
|
|
payload = virtual.parse_callback_body(
|
|
b"<xml><Event>xpay_refund_notify</Event><GoodsInfo><ActualPrice>1000</ActualPrice></GoodsInfo></xml>"
|
|
)
|
|
assert virtual.callback_value(payload, "event") == "xpay_refund_notify"
|
|
assert virtual.callback_value(payload, "goodsinfo", "actualprice") == "1000"
|
|
|
|
|
|
def test_xpay_request_uses_server_access_token_and_pay_signature():
|
|
token_response = Mock(status_code=200)
|
|
token_response.json.return_value = {"access_token": "server-token", "expires_in": 7200}
|
|
pay_response = Mock(status_code=200)
|
|
pay_response.json.return_value = {"errcode": 0, "order": {"status": 2}}
|
|
virtual._access_token_cache = ("", 0)
|
|
env = {
|
|
"WECHAT_MP_APP_ID": "wx-app",
|
|
"WECHAT_MP_APP_SECRET": "wx-secret",
|
|
"WECHAT_VIRTUAL_SANDBOX_APP_KEY": "sandbox-key",
|
|
"WECHAT_VIRTUAL_ENV": "sandbox",
|
|
}
|
|
with patch.dict(os.environ, env), patch.object(virtual.httpx, "get", return_value=token_response), patch.object(
|
|
virtual.httpx, "post", return_value=pay_response
|
|
) as post:
|
|
result = virtual.query_order(openid="openid", order_no="AV1")
|
|
assert result["order"]["status"] == 2
|
|
body = '{"openid":"openid","order_id":"AV1","env":1}'
|
|
expected = hmac.new(b"sandbox-key", f"/xpay/query_order&{body}".encode(), hashlib.sha256).hexdigest()
|
|
assert post.call_args.args[0] == "https://api.weixin.qq.com/xpay/query_order"
|
|
assert post.call_args.kwargs["params"] == {"access_token": "server-token", "pay_sig": expected}
|