@@ -99,6 +99,11 @@ urlpatterns += [ |
||
| 99 | 99 |
url(r'^wx/notify_url$', pay_views.wx_notify_url_api, name='wx_notify_url_api'), # 支付异步通知回调地址 |
| 100 | 100 |
] |
| 101 | 101 |
|
| 102 |
+# 提现相关 |
|
| 103 |
+urlpatterns += [ |
|
| 104 |
+ url(r'^wx/balance_withdraw$', pay_views.wx_balance_withdraw_api, name='wx_balance_withdraw_api'), # 余额提现: 企业付款/现金红包 |
|
| 105 |
+] |
|
| 106 |
+ |
|
| 102 | 107 |
# 分享相关 |
| 103 | 108 |
urlpatterns += [ |
| 104 | 109 |
url(r'^wx/jsapi_signature$', wechat_views.wx_jsapi_signature_api, name='wx_jsapi_signature_api'), # jsapi_signature |
@@ -261,6 +261,11 @@ WECHAT = {
|
||
| 261 | 261 |
'appsecret': '', |
| 262 | 262 |
'mchID': '', |
| 263 | 263 |
'apiKey': '', |
| 264 |
+ 'mch_cert': '', |
|
| 265 |
+ 'mch_key': '', |
|
| 266 |
+ 'redpacket': {
|
|
| 267 |
+ |
|
| 268 |
+ } |
|
| 264 | 269 |
}, |
| 265 | 270 |
} |
| 266 | 271 |
|
@@ -13,7 +13,7 @@ from account.models import LensmanIncomeExpensesInfo, LensmanInfo, UserIncomeExp |
||
| 13 | 13 |
from group.models import GroupPhotoInfo, GroupPhotoOrderInfo |
| 14 | 14 |
from pay.models import OrderInfo |
| 15 | 15 |
from photo.models import PhotosInfo |
| 16 |
-from utils.error.errno_utils import GroupPhotoStatusCode, OrderStatusCode |
|
| 16 |
+from utils.error.errno_utils import GroupPhotoStatusCode, OrderStatusCode, UserStatusCode, WithdrawStatusCode |
|
| 17 | 17 |
from utils.error.response_utils import response |
| 18 | 18 |
from utils.page_utils import pagination |
| 19 | 19 |
from utils.redis.rkeys import LENSMAN_PHOTO_PRICE |
@@ -329,3 +329,42 @@ def wx_notify_url_api(request): |
||
| 329 | 329 |
order_paid_success(order) |
| 330 | 330 |
|
| 331 | 331 |
return HttpResponse(settings.WXPAY_NOTIFY_SUCCESS) |
| 332 |
+ |
|
| 333 |
+ |
|
| 334 |
+def wx_balance_withdraw_api(request): |
|
| 335 |
+ user_id = request.POST.get('user_id', '')
|
|
| 336 |
+ |
|
| 337 |
+ # 用户校验 |
|
| 338 |
+ try: |
|
| 339 |
+ user = UserInfo.objects.get(user_id=user_id) |
|
| 340 |
+ except UserInfo.DoesNotExist: |
|
| 341 |
+ return response(UserStatusCode.USER_NOT_FOUND) |
|
| 342 |
+ |
|
| 343 |
+ # JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里 |
|
| 344 |
+ trade_type = request.POST.get('trade_type', '')
|
|
| 345 |
+ # TRANSFER--企业付款、PACKET--现金红包, 余额提现接口withdraw_type的传参可参考这里 |
|
| 346 |
+ withdraw_type = request.POST.get('withdraw_type', 'TRANSFER')
|
|
| 347 |
+ amount = int(request.POST.get('amount', 0))
|
|
| 348 |
+ |
|
| 349 |
+ if user.balance < amount: |
|
| 350 |
+ return response(WithdrawStatusCode.BALANCE_NOT_ENOUGH) |
|
| 351 |
+ |
|
| 352 |
+ # 根据 trade_type 获取 wechat 配置 |
|
| 353 |
+ wechat = WECHAT.get(trade_type, {})
|
|
| 354 |
+ # WeChatPay 初始化 |
|
| 355 |
+ wxpay = WeChatPay(wechat.get('appID'), wechat.get('apiKey'), wechat.get('mchID'), mch_cert=wechat.get('mch_cert'), mch_key=wechat.get('mch_key'))
|
|
| 356 |
+ |
|
| 357 |
+ if withdraw_type == 'TRANSFER': |
|
| 358 |
+ wxpay.transfer.transfer(user.wx_uid, amount, u'摄影师余额提现,企业付款', check_name='NO_CHECK') |
|
| 359 |
+ elif withdraw_type == 'PACKET': |
|
| 360 |
+ wxpay.redpack.send( |
|
| 361 |
+ user.wx_uid, |
|
| 362 |
+ amount, |
|
| 363 |
+ send_name=wechat.get('redpacket', {}).get('SEND_NAME'),
|
|
| 364 |
+ nick_name=wechat.get('redpacket', {}).get('NICK_NAME'),
|
|
| 365 |
+ act_name=wechat.get('redpacket', {}).get('ACT_NAME'),
|
|
| 366 |
+ wishing=wechat.get('redpacket', {}).get('WISHING'),
|
|
| 367 |
+ remark=wechat.get('redpacket', {}).get('REMARK'),
|
|
| 368 |
+ ) |
|
| 369 |
+ |
|
| 370 |
+ return response(200, 'Withdraw Success', u'提现成功', {})
|
@@ -75,6 +75,11 @@ class OrderStatusCode(BaseStatusCode): |
||
| 75 | 75 |
NO_DETAIL_PERMISSION = StatusCodeField(404015, u'No Detail Permission', description=u'没有详情权限') |
| 76 | 76 |
|
| 77 | 77 |
|
| 78 |
+class WithdrawStatusCode(BaseStatusCode): |
|
| 79 |
+ """ 提现相关错误码 4041xx """ |
|
| 80 |
+ BALANCE_NOT_ENOUGH = StatusCodeField(404100, u'Balance Not Enough', description=u'提现金额不足') |
|
| 81 |
+ |
|
| 82 |
+ |
|
| 78 | 83 |
class MessageStatusCode(BaseStatusCode): |
| 79 | 84 |
""" 消息相关错误码 4090xx """ |
| 80 | 85 |
MESSAGE_NOT_FOUND = StatusCodeField(409001, u'Message Not Found', description=u'消息不存在') |
@@ -2,11 +2,18 @@ |
||
| 2 | 2 |
|
| 3 | 3 |
from django.http import JsonResponse |
| 4 | 4 |
|
| 5 |
+from utils.error.errno_utils import StatusCodeField |
|
| 5 | 6 |
|
| 6 |
-def response(status_code, data={}):
|
|
| 7 |
- return JsonResponse({
|
|
| 7 |
+ |
|
| 8 |
+def response_data(status_code, message=None, description=None, data={}):
|
|
| 9 |
+ return {
|
|
| 8 | 10 |
'status': status_code, |
| 9 |
- 'message': status_code.message, |
|
| 10 |
- 'description': status_code.description, |
|
| 11 |
+ 'message': message, |
|
| 12 |
+ 'description': description, |
|
| 11 | 13 |
'data': data, |
| 12 |
- }) |
|
| 14 |
+ } |
|
| 15 |
+ |
|
| 16 |
+ |
|
| 17 |
+def response(status_code, message=None, description=None, data={}):
|
|
| 18 |
+ message, description = (status_code.message, status_code.description) if isinstance(status_code, StatusCodeField) else (message, description) |
|
| 19 |
+ return JsonResponse(response_data(status_code, message, description, data)) |