# Python 3.9+；只使用标准库。
import hashlib, hmac, json, os, secrets, time
from urllib.request import Request, build_opener, HTTPRedirectHandler
from urllib.error import HTTPError
class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None
base_url = os.environ['OPEN_API_BASE_URL'].rstrip('/')
api_key = os.environ['OPEN_API_KEY']
secret = os.environ['OPEN_API_SECRET']
path = '/api/v1/open/viewers/upsert'
raw_body = "{\"externalUserId\":\"customer-10001\",\"phone\":\"13800138000\",\"displayName\":\"张三\"}".encode('utf-8')
timestamp, nonce = str(int(time.time())), secrets.token_hex(16)
canonical = '\n'.join([api_key, 'POST', path, timestamp, nonce, hashlib.sha256(raw_body).hexdigest()])
signature = hmac.new(secret.encode('utf-8'), canonical.encode('utf-8'), hashlib.sha256).hexdigest()
request = Request(base_url + path, data=raw_body, method='POST', headers={
    'Content-Type': 'application/json', 'X-API-Key': api_key,
    'X-Timestamp': timestamp, 'X-Nonce': nonce, 'X-Signature': signature,
})
try:
    response = build_opener(NoRedirect).open(request, timeout=15)
except HTTPError as error:
    response = error
result = json.load(response)
# 将 result['data'] 交给业务处理；不要记录完整响应和票据。
print({'status': response.code, 'requestId': result.get('requestId'), 'errorCode': result.get('error', {}).get('code')})
