最近公司项目需要做个支付,首先考虑的就是大众化的支付宝支付和微信支付,其中也遇见了一些坑,在这里就记录下来:

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'支付失败')