一、
FreeCourse文件



二、前台支付成功頁面
1、
views/PaySuccess.vue ```vue <template> <div class="pay-success"> <!--如果是單獨的頁面,就沒必要展示導航欄(帶有登錄的用戶)--> <Header/> <div class="main"> <div class="title"> <div class="success-tips"> <p class="tips">您已成功購買 1 門課程!</p> </div> </div> <div class="order-info"> <p class="info"><b>訂單號:</b><span>{{ result.out_trade_no }}</span></p> <p class="info"><b>交易號:</b><span>{{ result.trade_no }}</span></p> <p class="info"><b>付款時間:</b><span><span>{{ result.timestamp }}</span></span></p> </div> <div class="study"> <span>立即學習</span> </div> </div> </div> </template> <script> import Header from "@/components/Header" export default { name: "Success", data() { return { result: {}, }; }, created() { // url后拼接的參數:?及后面的所有參數 => ?a=1&b=2 // console.log(location.search); // 解析支付寶回調的url參數 let params = location.search.substring(1); // 去除? => a=1&b=2 let items = params.length ? params.split('&') : []; // ['a=1', 'b=2'] //逐個將每一項添加到args對象中 for (let i = 0; i < items.length; i++) { // 第一次循環a=1,第二次b=2 let k_v = items[i].split('='); // ['a', '1'] //解碼操作,因為查詢字符串經過編碼的 if (k_v.length >= 2) { // url編碼反解 let k = decodeURIComponent(k_v[0]); this.result[k] = decodeURIComponent(k_v[1]); // 沒有url編碼反解 // this.result[k_v[0]] = k_v[1]; } } // 解析后的結果 // console.log(this.result); // 把地址欄上面的支付結果,再get請求轉發給后端 this.$axios({ url: this.$settings.base_url + '/order/success/' + location.search, method: 'get', }).then(response => { console.log(response.data); }).catch(() => { console.log('支付結果同步失敗'); }) }, components: { Header, } } </script> <style scoped> .main { padding: 60px 0; margin: 0 auto; width: 1200px; background: #fff; } .main .title { display: flex; -ms-flex-align: center; align-items: center; padding: 25px 40px; border-bottom: 1px solid #f2f2f2; } .main .title .success-tips { box-sizing: border-box; } .title img { vertical-align: middle; width: 60px; height: 60px; margin-right: 40px; } .title .success-tips { box-sizing: border-box; } .title .tips { font-size: 26px; color: #000; } .info span { color: #ec6730; } .order-info { padding: 25px 48px; padding-bottom: 15px; border-bottom: 1px solid #f2f2f2; } .order-info p { display: -ms-flexbox; display: flex; margin-bottom: 10px; font-size: 16px; } .order-info p b { font-weight: 400; color: #9d9d9d; white-space: nowrap; } .study { padding: 25px 40px; } .study span { display: block; width: 140px; height: 42px; text-align: center; line-height: 42px; cursor: pointer; background: #ffc210; border-radius: 6px; font-size: 16px; color: #fff; } </style> ```
2、路由注冊

三、回調接口
order/views文件
from utils.logging import logger from utils.response import APIResponse class SuccessViewSet(ViewSet): authentication_classes = () permission_classes = () # 支付寶同步回調給前台,在同步通知給后台處理 def get(self, request, *args, **kwargs): # return Response('后台已知曉,Over!!!') # 不能在該接口完成訂單修改操作 # 但是可以在該接口中校驗訂單狀態(已經收到支付寶post異步通知,訂單已修改),告訴前台 # print(type(request.query_params)) # django.http.request.QueryDict # print(type(request.query_params.dict())) # dict out_trade_no = request.query_params.get('out_trade_no') try: models.Order.objects.get(out_trade_no=out_trade_no, order_status=1) return APIResponse(result=True) except: return APIResponse(1, 'error', result=False) # 支付寶異步回調處理 def post(self, request, *args, **kwargs): try: result_data = request.data.dict() out_trade_no = result_data.get('out_trade_no') signature = result_data.pop('sign') from libs import iPay result = iPay.alipay.verify(result_data, signature) if result and result_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"): # 完成訂單修改:訂單狀態、流水號、支付時間 models.Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1) # 完成日志記錄 logger.warning('%s訂單支付成功' % out_trade_no) return Response('success') else: logger.error('%s訂單支付失敗' % out_trade_no) except: pass return Response('failed')
