最近公司项目需要做个支付,首先考虑的就是大众化的支付宝支付和微信支付,其中也遇见了一些坑,在这里就记录下来:
1:支付宝支付:
alipay.py文件:
# -*- coding: utf-8 -*-
import types
import datetime
from urllib import urlencode, urlopen
from decimal import Decimal as D
from hashcompat import md5_constructor as md5
from config import settings
from pay.models import AlipayBill
def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
Returns a bytestring version of 's', encoded as specified in 'encoding'.
If strings_only is True, don't convert (some) non-string-like objects.
"""
if strings_only and isinstance(s, (types.NoneType, int)):
return s
if not isinstance(s, basestring):
try:
return str(s)
except UnicodeEncodeError:
if isinstance(s, Exception):
# An Exception subclass containing non-ASCII data that doesn't
# know how to print itself properly. We shouldn't raise a
# further exception.
return ' '.join([smart_str(arg, encoding, strings_only,
errors) for arg in s])
return unicode(s).encode(encoding, errors)
elif isinstance(s, unicode):
return s.encode(encoding, errors)
elif s and encoding != 'utf-8':
return s.decode('utf-8', errors).encode(encoding, errors)
else:
return s
# 对数组排序并除去数组中的空值和签名参数
# 返回数组和链接串
def params_filter(params):
ks = params.keys()
ks.sort()
newparams = {}
prestr = ''
for k in ks:
v = params[k]
k = smart_str(k, settings.ALIPAY_INPUT_CHARSET)
if k not in ('sign','sign_type') and v != '':
newparams[k] = smart_str(v, settings.ALIPAY_INPUT_CHARSET)
prestr += '%s=%s&' % (k, newparams[k])
prestr = prestr[:-1]
return newparams, prestr
# 生成签名结果
def build_mysign(prestr, key, sign_type = 'MD5'):
if sign_type == 'MD5':
return md5(prestr + key).hexdigest()
return ''
# 网关地址
_GATEWAY = 'https://mapi.alipay.com/gateway.do?'
# 即时到账交易接口
def create_direct_pay_by_user( tn, subject, body,bank, total_fee):
params = {}
params['service'] = 'create_direct_pay_by_user'
params['payment_type'] = '1'
# 获取配置文件
params['partner'] = settings.ALIPAY_PARTNER
params['seller_email'] = settings.ALIPAY_SELLER_EMAIL
params['return_url'] = settings.ALIPAY_RETURN_URL
params['notify_url'] = settings.ALIPAY_NOTIFY_URL
params['_input_charset'] = settings.ALIPAY_INPUT_CHARSET
params['show_url'] = settings.ALIPAY_SHOW_URL
# 从订单数据中动态获取到的必填参数
params['out_trade_no'] = tn # 请与贵网站订单系统中的唯一订单号匹配
params['subject'] = subject # 订单名称,显示在支付宝收银台里的“商品名称”里,显示在支付宝的交易管理的“商品名称”的列表里。
params['body'] = body # 订单描述、订单详细、订单备注,显示在支付宝收银台里的“商品描述”里
params['total_fee'] = total_fee # 订单总金额,显示在支付宝收银台里的“应付总额”里
# 扩展功能参数——网银提前
params['paymethod'] = 'directPay' # 默认支付方式,四个值可选:bankPay(网银); cartoon(卡通); directPay(余额); CASH(网点支付)
params['defaultbank'] = '' # 默认网银代号,代号列表见http://club.alipay.com/read.php?tid=8681379
# 扩展功能参数——防钓鱼
params['anti_phishing_key'] = ''
params['exter_invoke_ip'] = ''
# 扩展功能参数——自定义参数
params['buyer_email'] = ''
params['extra_common_param'] = ''
# 扩展功能参数——分润
params['royalty_type'] = ''
params['royalty_parameters'] = ''
params,prestr = params_filter(params)
params['sign'] = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
params['sign_type'] = settings.ALIPAY_SIGN_TYPE
return _GATEWAY + urlencode(params)
def notify_verify(post):
# 初级验证--签名
_,prestr = params_filter(post)
mysign = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
if mysign != post.get('sign'):
return False
# 二级验证--查询支付宝服务器此条信息是否有效
params = {}
params['partner'] = settings.ALIPAY_PARTNER
params['notify_id'] = post.get('notify_id')
if settings.ALIPAY_TRANSPORT == 'https':
params['service'] = 'notify_verify'
gateway = 'https://mapi.alipay.com/gateway.do'
else:
gateway = 'http://notify.alipay.com/trade/notify_query.do'
veryfy_result = urlopen(gateway, urlencode(params)).read()
if veryfy_result.lower().strip() == 'true':
return True
return False
config.py文件:
#-*- coding:utf-8 -*-
class settings:
# 安全检验码,以数字和字母组成的32位字符
ALIPAY_KEY = '********************************'
ALIPAY_INPUT_CHARSET = 'utf-8'
# 合作身份者ID,以2088开头的16位纯数字
ALIPAY_PARTNER = '****************'
# 签约支付宝账号或卖家支付宝帐户
ALIPAY_SELLER_EMAIL = '*********'
ALIPAY_SIGN_TYPE = 'MD5'
# 付完款后跳转的页面(同步通知) 要用 http://格式的完整路径,不允许加?id=123这类自定义参数
ALIPAY_RETURN_URL='http://xxx.com/xxx/'
# 交易过程中服务器异步通知的页面 要用 http://格式的完整路径,不允许加?id=123这类自定义参数
ALIPAY_NOTIFY_URL='http://xxx.com/xxx/'
ALIPAY_SHOW_URL=''
# 访问模式,根据自己的服务器是否支持ssl访问,若支持请选择https;若不支持请选择http
ALIPAY_TRANSPORT='https'
hashcompat.py文件
"""
The md5 and sha modules are deprecated since Python 2.5, replaced by the
hashlib module containing both hash algorithms. Here, we provide a common
interface to the md5 and sha constructors, preferring the hashlib module when
available.
"""
try:
import hashlib
md5_constructor = hashlib.md5
md5_hmac = md5_constructor
sha_constructor = hashlib.sha1
sha_hmac = sha_constructor
except ImportError:
import md5
md5_constructor = md5.new
md5_hmac = md5
import sha
sha_constructor = sha.new
sha_hmac = sha
views.py文件:
@csrf_exempt
def go_pay(request):
time_utc = time.time()
tn= str(int(time_utc))+str(random.randint(100000,1000000))
subject = 'xxxxxxxxx'
body = 'xxxxxxxxxxx'
pay_url=create_direct_pay_by_user(tn,subject,body,bank,total_fee)
# 异步通知
@csrf_exempt
def get_pay_result(request):
if request.method == "POST":
if notify_verify(reqeust.POST):
ab.trade_no = trade_no
ab.trade_status = trade_status
ab.save()
if ab.money != total_fee:
return HttpResponse ("fail")
return HttpResponse ("success")
@csrf_exempt
def return_url_handler (request):
'''
@desc 支付宝异步付款成功返回页面
成功返回参数GET;<QueryDict: {
u'body': [u'1435806434784794'],
u'seller_email': [u'1677784292@qq.com'],
u'seller_id': [u'2088011717304370'],
u'trade_status': [u'TRADE_SUCCESS'],
u'trade_no': [u'2015070200001000660058128969'],
u'buyer_email': [u'669090202@qq.com'],
u'sign': [u'3df6b6d070f6c32cdec59dd1475309e6'],
u'notify_type': [u'trade_status_sync'],
u'exterface': [u'create_direct_pay_by_user'],
u'out_trade_no': [u'20150702110717100362'],
u'payment_type': [u'1'],
u'total_fee': [u'0.01'],
u'sign_type': [u'MD5'],
u'is_success': [u'T'],
u'subject': [u'\u8d2d\u4e70\u5546\u54c1\u8ba2\u5355\u94b1\u591a\u7684\u597d\u5904\u554a'],
u'notify_id': [u'RqPnCoPT3K9%2Fvwbh3InSM1eOjR0B18aDR3DemD61TohFmx%2BMP%2BmchwlYgtr1bqKvaeHq'],
u'notify_time': [u'2015-07-02 11:11:51'],
u'buyer_id': [u'2088402807932663']}>'''
if notify_verify(request.GET):
#这里可以做一些支付成功后的操作
return HttpResponseRedirect('/')
return HttpResponse(u'支付失败')