增加支付宝充值

pull/16/head
youngS 4 years ago
parent cae0715e7a
commit e2d8f60640
  1. 8
      fir_admin/src/api/app.js
  2. 28
      fir_admin/src/views/appinfos/list.vue
  3. 11
      fir_client/src/components/apps/FirApps.vue
  4. 2
      fir_client/src/components/user/FirUserOrders.vue
  5. 4
      fir_ser/admin/views/app.py
  6. 102
      fir_ser/api/base_views.py
  7. 1
      fir_ser/api/migrations/0037_auto_20210412_1559.py
  8. 1
      fir_ser/api/migrations/0038_auto_20210412_1601.py
  9. 1
      fir_ser/api/migrations/0039_auto_20210412_1759.py
  10. 18
      fir_ser/api/migrations/0040_auto_20210413_1010.py
  11. 2
      fir_ser/api/models.py
  12. 3
      fir_ser/api/urls.py
  13. 861
      fir_ser/api/utils/alipay/__init__.py
  14. 10
      fir_ser/api/utils/alipay/compat.py
  15. 26
      fir_ser/api/utils/alipay/exceptions.py
  16. 26
      fir_ser/api/utils/alipay/loggers.py
  17. 10
      fir_ser/api/utils/alipay/utils.py
  18. 73
      fir_ser/api/utils/pay/ali.py
  19. 39
      fir_ser/api/utils/storage/caches.py
  20. 27
      fir_ser/api/views/apps.py
  21. 28
      fir_ser/api/views/order.py
  22. 20
      fir_ser/fir_ser/settings.py

@ -16,6 +16,14 @@ export function updateAppInfo(data) {
})
}
export function deleteApp(data) {
return request({
url: '/app/info',
method: 'delete',
data
})
}
export function getAppReleaseInfos(query) {
return request({
url: '/app/release/info',

@ -50,7 +50,7 @@
</el-table-column>
<el-table-column label="关联应用" align="center" width="130">
<template slot-scope="scope">
<el-image v-if="scope.row.has_combo" :src="scope.row.has_combo.master_release.icon_url" :preview-src-list="[scope.row.has_combo.master_release.icon_url]" fit="contain" style="width: 80px; height: 80px" />
<el-image v-if="scope.row.has_combo" :src="scope.row.has_combo.master_release.icon_url" :preview-src-list="[scope.row.has_combo.master_release.icon_url]" fit="contain" style="width: 80px; height: 80px" />
<el-link v-else>无关联应用</el-link>
</template>
</el-table-column>
@ -115,7 +115,7 @@
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini">
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
删除
</el-button>
</template>
@ -127,7 +127,7 @@
</template>
<script>
import { getAppInfos } from '@/api/app'
import { getAppInfos, deleteApp } from '@/api/app'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
@ -169,7 +169,7 @@ export default {
'2': 'gray'
}
return statusMap[status]
},
}
},
data() {
return {
@ -195,6 +195,26 @@ export default {
this.fetchData()
},
methods: {
deleteApp(app_id) {
this.$confirm('此操作将永久删除该应用, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.listLoading = true
deleteApp({ id: app_id }).then(response => {
this.$message.success('删除成功')
this.fetchData()
this.listLoading = false
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()

@ -534,12 +534,11 @@
this.buy_button_disable = true;
my_order(res => {
if (res.code === 1000) {
this.$message.success("下订单成功,正在跳转,请去我的订单进行支付");
// eslint-disable-next-line no-unused-vars
this.timmer = setTimeout(data => {
this.$router.push({"name": 'FirUserOrders'});
this.buy_button_disable = false;
}, 1000);
this.$message.success("下订单成功,正在跳转支付平台");
let pay_url = res.data;
if(pay_url && pay_url.length > 10){
window.location.href = pay_url
}
} else {
this.$message.error("异常" + res.msg);
this.buy_button_disable = false;

@ -224,6 +224,7 @@
<script>
import {my_order} from "@/restful";
import {getUserInfoFun} from '@/utils'
export default {
name: "FirUserOrders",
@ -365,6 +366,7 @@
}, {methods: 'GET', data: params})
},
}, mounted() {
getUserInfoFun(this);
this.get_data_from_tabname()
}, filters: {}
}

@ -20,6 +20,7 @@ from api.utils.storage.caches import login_auth_failed, del_cache_response_by_sh
import logging
from api.utils.throttle import VisitRegister1Throttle, VisitRegister2Throttle
from rest_framework.pagination import PageNumberPagination
from api.base_views import app_delete
logger = logging.getLogger(__name__)
@ -80,7 +81,8 @@ class AppInfoView(APIView):
if not pk:
res.code = 1003
res.msg = "参数错误"
Apps.delete()
else:
res = app_delete(Apps.objects.filter(pk=pk).first())
return Response(res.dict)

@ -0,0 +1,102 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 4月
# author: NinEveN
# date: 2021/4/13
from rest_framework.views import APIView
from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication
from rest_framework.response import Response
from django.db.models import Sum
from api.utils.app.supersignutils import IosUtils, resign_by_app_obj
from api.utils.storage.storage import Storage
from api.utils.storage.caches import del_cache_response_by_short, get_app_today_download_times, del_cache_by_delete_app
from api.models import Apps, AppReleaseInfo, APPToDeveloper, AppIOSDeveloperInfo, UserInfo, AppScreenShot
from api.utils.serializer import AppsSerializer, AppReleaseSerializer
from rest_framework.pagination import PageNumberPagination
import logging
from fir_ser.settings import SERVER_DOMAIN
from api.utils.utils import is_valid_domain, delete_local_files, delete_app_screenshots_files
logger = logging.getLogger(__name__)
def app_delete(app_obj):
res = BaseResponse()
if not app_obj:
res.code = 1001
res.msg = "应用不存在"
return res
user_obj = app_obj.user_id
count = APPToDeveloper.objects.filter(app_id=app_obj).count()
if app_obj.issupersign or count > 0:
logger.info("app_id:%s is supersign ,delete this app need clean IOS developer" % (app_obj.app_id))
IosUtils.clean_app_by_user_obj(app_obj, user_obj)
storage = Storage(user_obj)
has_combo = app_obj.has_combo
if has_combo:
logger.info(
"app_id:%s has_combo ,delete this app need uncombo and clean del_cache_response_by_short" % (
app_obj.app_id))
has_combo.has_combo = None
del_cache_response_by_short(app_obj.app_id)
del_cache_by_delete_app(app_obj.app_id)
for app_release_obj in AppReleaseInfo.objects.filter(app_id=app_obj).all():
logger.info("delete app_id:%s need clean all release,release_id:%s" % (
app_obj.app_id, app_release_obj.release_id))
storage.delete_file(app_release_obj.release_id, app_release_obj.release_type)
delete_local_files(app_release_obj.release_id, app_release_obj.release_type)
storage.delete_file(app_release_obj.icon_url)
app_release_obj.delete()
delete_app_screenshots_files(storage, app_obj)
app_obj.delete()
return res
def app_screen_delete(screen_id, apps_obj, storage):
screen_obj = AppScreenShot.objects.filter(pk=screen_id, app_id=apps_obj).first()
if screen_obj:
storage.delete_file(screen_obj.screenshot_url)
screen_obj.delete()
del_cache_response_by_short(apps_obj.app_id)
def app_release_delete(app_obj, release_id, storage):
res = BaseResponse()
user_obj = app_obj.user_id
if app_obj:
apprelease_count = AppReleaseInfo.objects.filter(app_id=app_obj).values("release_id").count()
appreleaseobj = AppReleaseInfo.objects.filter(app_id=app_obj, release_id=release_id).first()
if not appreleaseobj.is_master:
logger.info("delete app release %s" % (appreleaseobj))
storage.delete_file(appreleaseobj.release_id, appreleaseobj.release_type)
delete_local_files(appreleaseobj.release_id, appreleaseobj.release_type)
storage.delete_file(appreleaseobj.icon_url)
appreleaseobj.delete()
elif appreleaseobj.is_master and apprelease_count < 2:
logger.info("delete app master release %s and clean app %s " % (appreleaseobj, app_obj))
count = APPToDeveloper.objects.filter(app_id=app_obj).count()
if app_obj.issupersign or count > 0:
logger.info("app_id:%s is supersign ,delete this app need clean IOS developer" % (app_obj.app_id))
IosUtils.clean_app_by_user_obj(app_obj, user_obj)
storage.delete_file(appreleaseobj.release_id, appreleaseobj.release_type)
delete_local_files(appreleaseobj.release_id, appreleaseobj.release_type)
storage.delete_file(appreleaseobj.icon_url)
del_cache_by_delete_app(app_obj.app_id)
appreleaseobj.delete()
delete_app_screenshots_files(storage, app_obj)
has_combo = app_obj.has_combo
if has_combo:
app_obj.has_combo.has_combo = None
app_obj.delete()
else:
pass
del_cache_response_by_short(app_obj.app_id)
return res

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0036_auto_20210409_1512'),
]

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0037_auto_20210412_1559'),
]

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0038_auto_20210412_1601'),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2021-04-13 10:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0039_auto_20210412_1759'),
]
operations = [
migrations.AlterField(
model_name='usercertificationinfo',
name='status',
field=models.SmallIntegerField(choices=[(-1, '待认证'), (0, '认证中'), (1, '认证成功'), (2, '认证失败')], default=0,
verbose_name='审核状态'),
),
]

@ -405,7 +405,7 @@ class UserCertificationInfo(models.Model):
card = models.CharField(max_length=128, null=False, verbose_name="身份证号码")
addr = models.CharField(max_length=128, null=False, verbose_name="居住地")
mobile = models.BigIntegerField(verbose_name="手机号码", unique=True, null=False)
status_choices = ((-1, '待认证'),(0, '认证中'), (1, '认证成功'), (2, '认证失败'))
status_choices = ((-1, '待认证'), (0, '认证中'), (1, '认证成功'), (2, '认证失败'))
status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="审核状态")
msg = models.CharField(max_length=512, null=True, blank=True, verbose_name="备注")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

@ -23,7 +23,7 @@ from api.views.download import ShortDownloadView
from api.views.uploads import AppAnalyseView, UploadView
from api.views.storage import StorageView
from api.views.receiveudids import IosUDIDView
from api.views.order import PriceView, OrderView
from api.views.order import PriceView, OrderView, PaySuccess
from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView
# router=DefaultRouter()
@ -52,5 +52,6 @@ urlpatterns = [
re_path("^package_prices$", PriceView.as_view()),
re_path("^orders$", OrderView.as_view()),
re_path("^certification$", CertificationView.as_view()),
re_path("^pay_success$", PaySuccess.as_view()),
]

@ -0,0 +1,861 @@
#!/usr/bin/env python
# coding: utf-8
"""
__init__.py
~~~~~~~~~~
"""
import json
from datetime import datetime
from functools import partial
import hashlib
import OpenSSL
from Cryptodome.Hash import SHA, SHA256
from Cryptodome.PublicKey import RSA
from Cryptodome.Signature import PKCS1_v1_5
from .compat import decodebytes, encodebytes, quote_plus, urlopen
from .exceptions import AliPayException, AliPayValidationError
from .utils import AliPayConfig
from .loggers import logger
# 常见加密算法
CryptoAlgSet = (
b'rsaEncryption',
b'md2WithRSAEncryption',
b'md5WithRSAEncryption',
b'sha1WithRSAEncryption',
b'sha256WithRSAEncryption',
b'sha384WithRSAEncryption',
b'sha512WithRSAEncryption'
)
class BaseAliPay:
@property
def appid(self):
return self._appid
@property
def sign_type(self):
return self._sign_type
@property
def app_private_key(self):
"""签名用"""
return self._app_private_key
@property
def alipay_public_key(self):
"""验证签名用"""
return self._alipay_public_key
def __init__(
self,
appid,
app_notify_url,
app_private_key_string=None,
alipay_public_key_string=None,
sign_type="RSA2",
debug=False,
verbose=False,
config=None
):
"""
初始化:
alipay = AliPay(
appid="",
app_notify_url="http://example.com",
sign_type="RSA2"
)
"""
self._appid = str(appid)
self._app_notify_url = app_notify_url
self._app_private_key_string = app_private_key_string
self._alipay_public_key_string = alipay_public_key_string
self._verbose = verbose
self._config = config or AliPayConfig()
self._app_private_key = None
self._alipay_public_key = None
if sign_type not in ("RSA", "RSA2"):
message = "Unsupported sign type {}".format(sign_type)
raise AliPayException(None, message)
self._sign_type = sign_type
if debug:
self._gateway = "https://openapi.alipaydev.com/gateway.do"
else:
self._gateway = "https://openapi.alipay.com/gateway.do"
# load key file immediately
self._load_key()
def _load_key(self):
# load private key
content = self._app_private_key_string
self._app_private_key = RSA.importKey(content)
# load public key
content = self._alipay_public_key_string
self._alipay_public_key = RSA.importKey(content)
def _sign(self, unsigned_string):
"""
通过如下方法调试签名
方法1
key = rsa.PrivateKey.load_pkcs1(open(self._app_private_key_string).read())
sign = rsa.sign(unsigned_string.encode(), key, "SHA-1")
# base64 编码,转换为unicode表示并移除回车
sign = base64.encodebytes(sign).decode().replace("\n", "")
方法2
key = RSA.importKey(open(self._app_private_key_string).read())
signer = PKCS1_v1_5.new(key)
signature = signer.sign(SHA.new(unsigned_string.encode()))
# base64 编码,转换为unicode表示并移除回车
sign = base64.encodebytes(signature).decode().replace("\n", "")
方法3
echo "abc" | openssl sha1 -sign alipay.key | openssl base64
"""
# 开始计算签名
key = self.app_private_key
signer = PKCS1_v1_5.new(key)
if self._sign_type == "RSA":
signature = signer.sign(SHA.new(unsigned_string.encode()))
else:
signature = signer.sign(SHA256.new(unsigned_string.encode()))
# base64 编码,转换为unicode表示并移除回车
sign = encodebytes(signature).decode().replace("\n", "")
return sign
def _ordered_data(self, data):
for k, v in data.items():
if isinstance(v, dict):
# 将字典类型的数据dump出来
data[k] = json.dumps(v, separators=(',', ':'))
return sorted(data.items())
def build_body(self, method, biz_content, **kwargs):
data = {
"app_id": self._appid,
"method": method,
"charset": "utf-8",
"sign_type": self._sign_type,
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"version": "1.0",
"biz_content": biz_content
}
data.update(kwargs)
if method in (
"alipay.trade.app.pay", "alipay.trade.wap.pay",
"alipay.trade.page.pay", "alipay.trade.pay",
"alipay.trade.precreate", "alipay.trade.create"
) and not data.get("notify_url") and self._app_notify_url:
data["notify_url"] = self._app_notify_url
if self._verbose:
logger.debug("data to be signed")
logger.debug(data)
return data
def sign_data(self, data):
# 排序后的字符串
ordered_items = self._ordered_data(data)
raw_string = "&".join("{}={}".format(k, v) for k, v in ordered_items)
sign = self._sign(raw_string)
unquoted_items = ordered_items + [('sign', sign)]
# 获得最终的订单信息字符串
signed_string = "&".join("{}={}".format(k, quote_plus(v)) for k, v in unquoted_items)
if self._verbose:
logger.debug("signed srtring")
logger.debug(signed_string)
return signed_string
def _verify(self, raw_content, signature):
# 开始计算签名
key = self.alipay_public_key
signer = PKCS1_v1_5.new(key)
if self._sign_type == "RSA":
digest = SHA.new()
else:
digest = SHA256.new()
digest.update(raw_content.encode())
return bool(signer.verify(digest, decodebytes(signature.encode())))
def verify(self, data, signature):
if "sign_type" in data:
sign_type = data.pop("sign_type")
if sign_type != self._sign_type:
raise AliPayException(None, "Unknown sign type: {}".format(sign_type))
# 排序后的字符串
unsigned_items = self._ordered_data(data)
message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
return self._verify(message, signature)
def client_api(self, api_name, biz_content, **kwargs):
"""
alipay api without http request
"""
data = self.build_body(api_name, biz_content, **kwargs)
return self.sign_data(data)
def server_api(self, api_name, biz_content, **kwargs):
"""
alipay api with http request
"""
data = self.build_body(api_name, biz_content, **kwargs)
# alipay.trade.query => alipay_trade_query_response
response_type = api_name.replace(".", "_") + "_response"
print(data)
return self.verified_sync_response(data, response_type)
def api_alipay_trade_wap_pay(
self, subject, out_trade_no, total_amount,
return_url=None, notify_url=None, **kwargs
):
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "QUICK_WAP_PAY"
}
biz_content.update(kwargs)
data = self.build_body(
"alipay.trade.wap.pay",
biz_content,
return_url=return_url,
notify_url=notify_url
)
return self.sign_data(data)
def api_alipay_trade_app_pay(
self, subject, out_trade_no, total_amount, notify_url=None, **kwargs
):
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "QUICK_MSECURITY_PAY"
}
biz_content.update(kwargs)
data = self.build_body("alipay.trade.app.pay", biz_content, notify_url=notify_url)
return self.sign_data(data)
def api_alipay_trade_page_pay(self, subject, out_trade_no, total_amount,
return_url=None, notify_url=None, **kwargs):
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "FAST_INSTANT_TRADE_PAY"
}
biz_content.update(kwargs)
data = self.build_body(
"alipay.trade.page.pay",
biz_content,
return_url=return_url,
notify_url=notify_url
)
return self.sign_data(data)
def api_alipay_trade_query(self, out_trade_no=None, trade_no=None):
"""
response = {
"alipay_trade_query_response": {
"trade_no": "2017032121001004070200176844",
"code": "10000",
"invoice_amount": "20.00",
"open_id": "20880072506750308812798160715407",
"fund_bill_list": [
{
"amount": "20.00",
"fund_channel": "ALIPAYACCOUNT"
}
],
"buyer_logon_id": "csq***@sandbox.com",
"send_pay_date": "2017-03-21 13:29:17",
"receipt_amount": "20.00",
"out_trade_no": "out_trade_no15",
"buyer_pay_amount": "20.00",
"buyer_user_id": "2088102169481075",
"msg": "Success",
"point_amount": "0.00",
"trade_status": "TRADE_SUCCESS",
"total_amount": "20.00"
},
"sign": ""
}
"""
assert (out_trade_no is not None) or (trade_no is not None), \
"Both trade_no and out_trade_no are None"
biz_content = {}
if out_trade_no:
biz_content["out_trade_no"] = out_trade_no
if trade_no:
biz_content["trade_no"] = trade_no
data = self.build_body("alipay.trade.query", biz_content)
response_type = "alipay_trade_query_response"
return self.verified_sync_response(data, response_type)
def api_alipay_trade_pay(
self, out_trade_no, scene, auth_code, subject, notify_url=None, **kwargs
):
"""
eg:
self.api_alipay_trade_pay(
out_trade_no,
"bar_code/wave_code",
auth_code,
subject,
total_amount=12,
discountable_amount=10
)
failed response = {
"alipay_trade_pay_response": {
"code": "40004",
"msg": "Business Failed",
"sub_code": "ACQ.INVALID_PARAMETER",
"sub_msg": "",
"buyer_pay_amount": "0.00",
"invoice_amount": "0.00",
"point_amount": "0.00",
"receipt_amount": "0.00"
},
"sign": ""
}
succeeded response =
{
"alipay_trade_pay_response": {
"trade_no": "2017032121001004070200176846",
"code": "10000",
"invoice_amount": "20.00",
"open_id": "20880072506750308812798160715407",
"fund_bill_list": [
{
"amount": "20.00",
"fund_channel": "ALIPAYACCOUNT"
}
],
"buyer_logon_id": "csq***@sandbox.com",
"receipt_amount": "20.00",
"out_trade_no": "out_trade_no18",
"buyer_pay_amount": "20.00",
"buyer_user_id": "2088102169481075",
"msg": "Success",
"point_amount": "0.00",
"gmt_payment": "2017-03-21 15:07:29",
"total_amount": "20.00"
},
"sign": ""
}
"""
assert scene in ("bar_code", "wave_code"), 'scene not in ("bar_code", "wave_code")'
biz_content = {
"out_trade_no": out_trade_no,
"scene": scene,
"auth_code": auth_code,
"subject": subject
}
biz_content.update(**kwargs)
data = self.build_body("alipay.trade.pay", biz_content, notify_url=notify_url)
response_type = "alipay_trade_pay_response"
return self.verified_sync_response(data, response_type)
def api_alipay_trade_refund(self, refund_amount, out_trade_no=None, trade_no=None, **kwargs):
biz_content = {
"refund_amount": refund_amount
}
biz_content.update(**kwargs)
if out_trade_no:
biz_content["out_trade_no"] = out_trade_no
if trade_no:
biz_content["trade_no"] = trade_no
data = self.build_body("alipay.trade.refund", biz_content)
response_type = "alipay_trade_refund_response"
return self.verified_sync_response(data, response_type)
def api_alipay_trade_cancel(self, out_trade_no=None, trade_no=None):
"""
response = {
"alipay_trade_cancel_response": {
"msg": "Success",
"out_trade_no": "out_trade_no15",
"code": "10000",
"retry_flag": "N"
}
}
"""
assert (out_trade_no is not None) or (trade_no is not None), \
"Both trade_no and out_trade_no are None"
biz_content = {}
if out_trade_no:
biz_content["out_trade_no"] = out_trade_no
if trade_no:
biz_content["trade_no"] = trade_no
data = self.build_body("alipay.trade.cancel", biz_content)
response_type = "alipay_trade_cancel_response"
return self.verified_sync_response(data, response_type)
def api_alipay_trade_close(self, out_trade_no=None, trade_no=None, operator_id=None):
"""
response = {
"alipay_trade_close_response": {
"code": "10000",
"msg": "Success",
"trade_no": "2013112111001004500000675971",
"out_trade_no": "YX_001"a
}
}
"""
assert (out_trade_no is not None) or (trade_no is not None), \
"Both trade_no and out_trade_no are None"
biz_content = {}
if out_trade_no:
biz_content["out_trade_no"] = out_trade_no
if trade_no:
biz_content["trade_no"] = trade_no
if operator_id:
biz_content["operator_id"] = operator_id
data = self.build_body("alipay.trade.close", biz_content)
response_type = "alipay_trade_close_response"
return self.verified_sync_response(data, response_type)
def api_alipay_trade_create(
self, subject, out_trade_no, total_amount, notify_url=None, **kwargs
):
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount
}
biz_content.update(kwargs)
data = self.build_body("alipay.trade.create", biz_content, notify_url=notify_url)
response_type = "alipay_trade_create"
return self.verified_sync_response(data, response_type)
def api_alipay_trade_precreate(
self, subject, out_trade_no, total_amount, notify_url=None, **kwargs
):
"""
success response = {
"alipay_trade_precreate_response": {
"msg": "Success",
"out_trade_no": "out_trade_no17",
"code": "10000",
"qr_code": "https://qr.alipay.com/bax03431ljhokirwl38f00a7"
},
"sign": ""
}
failed response = {
"alipay_trade_precreate_response": {
"msg": "Business Failed",
"sub_code": "ACQ.TOTAL_FEE_EXCEED",
"code": "40004",
"sub_msg": "订单金额超过限额"
},
"sign": ""
}
"""
biz_content = {
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"subject": subject
}
biz_content.update(**kwargs)
data = self.build_body("alipay.trade.precreate", biz_content, notify_url=notify_url)
response_type = "alipay_trade_precreate_response"
return self.verified_sync_response(data, response_type)
def api_alipay_trade_fastpay_refund_query(
self, out_request_no, trade_no=None, out_trade_no=None
):
assert (out_trade_no is not None) or (trade_no is not None), \
"Both trade_no and out_trade_no are None"
biz_content = {"out_request_no": out_request_no}
if trade_no:
biz_content["trade_no"] = trade_no
else:
biz_content["out_trade_no"] = out_trade_no
data = self.build_body("alipay.trade.fastpay.refund.query", biz_content)
response_type = "alipay_trade_fastpay_refund_query_response"
return self.verified_sync_response(data, response_type)
def api_alipay_fund_trans_toaccount_transfer(
self, out_biz_no, payee_type, payee_account, amount, **kwargs
):
assert payee_type in ("ALIPAY_USERID", "ALIPAY_LOGONID"), "unknown payee type"
biz_content = {
"out_biz_no": out_biz_no,
"payee_type": payee_type,
"payee_account": payee_account,
"amount": amount
}
biz_content.update(kwargs)
data = self.build_body("alipay.fund.trans.toaccount.transfer", biz_content)
response_type = "alipay_fund_trans_toaccount_transfer_response"
return self.verified_sync_response(data, response_type)
def api_alipay_fund_trans_order_query(self, out_biz_no=None, order_id=None):
if out_biz_no is None and order_id is None:
raise Exception("Both out_biz_no and order_id are None!")
biz_content = {}
if out_biz_no:
biz_content["out_biz_no"] = out_biz_no
if order_id:
biz_content["order_id"] = order_id
data = self.build_body("alipay.fund.trans.order.query", biz_content)
response_type = "alipay_fund_trans_order_query_response"
return self.verified_sync_response(data, response_type)
def api_alipay_trade_order_settle(
self,
out_request_no,
trade_no,
royalty_parameters,
**kwargs
):
biz_content = {
"out_request_no": out_request_no,
"trade_no": trade_no,
"royalty_parameters": royalty_parameters,
}
biz_content.update(kwargs)
data = self.build_body("alipay.trade.order.settle", biz_content)
response_type = "alipay_trade_order_settle_response"
return self.verified_sync_response(data, response_type)
def api_alipay_ebpp_invoice_token_batchquery(self, invoice_token=None, scene=None):
if scene is None:
scene = "INVOICE_EXPENSE"
if invoice_token is None:
raise Exception("invoice_token is None!")
biz_content = {
"invoice_token": invoice_token,
"scene": scene
}
data = self.build_body("alipay.ebpp.invoice.token.batchquery", biz_content)
response_type = "alipay_ebpp_invoice_token_batchquery_response"
return self.verified_sync_response(data, response_type)
def _verify_and_return_sync_response(self, raw_string, response_type):
"""
return response if verification succeeded, raise exception if not
As to issue #69, json.loads(raw_string)[response_type] should not be returned directly,
use json.loads(plain_content) instead
failed response is like this
{
"alipay_trade_query_response": {
"sub_code": "isv.invalid-app-id",
"code": "40002",
"sub_msg": "无效的AppID参数",
"msg": "Invalid Arguments"
}
}
"""
response = json.loads(raw_string)
# raise exceptions
if "sign" not in response.keys():
result = response[response_type]
raise AliPayException(
code=result.get("code", "0"),
message=raw_string
)
sign = response["sign"]
# locate string to be signed
plain_content = self._get_string_to_be_signed(raw_string, response_type)
if not self._verify(plain_content, sign):
raise AliPayValidationError
return json.loads(plain_content)
def verified_sync_response(self, data, response_type):
url = self._gateway + "?" + self.sign_data(data)
raw_string = urlopen(url, timeout=self._config.timeout).read().decode()
return self._verify_and_return_sync_response(raw_string, response_type)
def _get_string_to_be_signed(self, raw_string, response_type):
"""
https://docs.open.alipay.com/200/106120
从同步返回的接口里面找到待签名的字符串
"""
balance = 0
start = end = raw_string.find("{", raw_string.find(response_type))
# 从response_type之后的第一个{的下一位开始匹配,
# 如果是{则balance加1; 如果是}而且balance=0,就是待验签字符串的终点
for i, c in enumerate(raw_string[start + 1:], start + 1):
if c == "{":
balance += 1
elif c == "}":
if balance == 0:
end = i + 1
break
balance -= 1
return raw_string[start:end]
class AliPay(BaseAliPay):
pass
class DCAliPay(BaseAliPay):
"""
数字证书 (digital certificate) 版本
"""
def __init__(
self,
appid,
app_notify_url,
app_private_key_string,
app_public_key_cert_string,
alipay_public_key_cert_string,
alipay_root_cert_string,
sign_type="RSA2",
debug=False
):
"""
初始化
DCAlipay(
appid='',
app_notify_url='http://example.com',
app_private_key_string='',
app_public_key_cert_string='',
alipay_public_key_cert_sring='',
aplipay_root_cert_string='',
)
"""
self._app_public_key_cert_string = app_public_key_cert_string
self._alipay_public_key_cert_string = alipay_public_key_cert_string
self._alipay_root_cert_string = alipay_root_cert_string
alipay_public_key_string = self.load_alipay_public_key_string()
super().__init__(
appid=appid,
app_notify_url=app_notify_url,
app_private_key_string=app_private_key_string,
alipay_public_key_string=alipay_public_key_string,
sign_type=sign_type,
debug=debug
)
def api_alipay_open_app_alipaycert_download(self, alipay_cert_sn):
"""
下载支付宝证书
验签使用支付宝公钥证书无感知升级机制
"""
biz_content = {
"alipay_cert_sn": alipay_cert_sn
}
data = self.build_body("alipay.open.app.alipaycert.download", biz_content)
return self.sign_data(data)
def build_body(self, *args, **kwargs):
data = super().build_body(*args, **kwargs)
data["app_cert_sn"] = self.app_cert_sn
data["alipay_root_cert_sn"] = self.alipay_root_cert_sn
if self._verbose:
logger.debug("data to be signed")
logger.debug(data)
return data
def load_alipay_public_key_string(self):
cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, self._alipay_public_key_cert_string
)
return OpenSSL.crypto.dump_publickey(
OpenSSL.crypto.FILETYPE_PEM, cert.get_pubkey()
).decode("utf-8")
@staticmethod
def get_cert_sn(cert):
"""
获取证书 SN 算法
"""
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
certIssue = cert.get_issuer()
name = 'CN={},OU={},O={},C={}'.format(certIssue.CN, certIssue.OU, certIssue.O, certIssue.C)
string = name + str(cert.get_serial_number())
return hashlib.md5(string.encode()).hexdigest()
@staticmethod
def read_pem_cert_chain(certContent):
"""解析根证书"""
# 根证书中,每个 cert 中间有两个回车间隔
items = [i for i in certContent.split('\n\n') if i]
load_cert = partial(OpenSSL.crypto.load_certificate, OpenSSL.crypto.FILETYPE_PEM)
return [load_cert(c) for c in items]
@staticmethod
def get_root_cert_sn(rootCert):
""" 根证书 SN 算法"""
certs = DCAliPay.read_pem_cert_chain(rootCert)
rootCertSN = None
for cert in certs:
try:
sigAlg = cert.get_signature_algorithm()
except ValueError:
continue
if sigAlg in CryptoAlgSet:
certIssue = cert.get_issuer()
name = 'CN={},OU={},O={},C={}'.format(
certIssue.CN, certIssue.OU, certIssue.O, certIssue.C
)
string = name + str(cert.get_serial_number())
certSN = hashlib.md5(string.encode()).hexdigest()
if not rootCertSN:
rootCertSN = certSN
else:
rootCertSN = rootCertSN + '_' + certSN
return rootCertSN
@property
def app_cert_sn(self):
if not hasattr(self, "_app_cert_sn"):
self._app_cert_sn = self.get_cert_sn(self._app_public_key_cert_string)
return getattr(self, "_app_cert_sn")
@property
def alipay_root_cert_sn(self):
if not hasattr(self, "_alipay_root_cert_sn"):
self._alipay_root_cert_sn = self.get_root_cert_sn(self._alipay_root_cert_string)
return getattr(self, "_alipay_root_cert_sn")
def api_alipay_fund_trans_uni_transfer(
self, out_biz_no, identity_type, identity, trans_amount, name=None, **kwargs
):
"""
单笔转账接口, 只支持公钥证书模式
文档地址: https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer
"""
assert identity_type in ("ALIPAY_USER_ID", "ALIPAY_LOGON_ID"), "unknown identity type"
biz_content = {
"payee_info": {
"identity": identity,
"identity_type": identity_type,
},
"out_biz_no": out_biz_no,
"trans_amount": trans_amount,
"product_code": "TRANS_ACCOUNT_NO_PWD",
"biz_scene": "DIRECT_TRANSFER",
}
biz_content["payee_info"]["name"] = name if name else None
biz_content.update(kwargs)
response_type = "alipay_fund_trans_uni_transfer_response"
data = self.build_body("alipay.fund.trans.uni.transfer", biz_content)
return self.verified_sync_response(data, response_type)
class ISVAliPay(BaseAliPay):
def __init__(
self,
appid,
app_notify_url,
app_private_key_string=None,
alipay_public_key_string=None,
sign_type="RSA2",
debug=False,
app_auth_token=None,
app_auth_code=None
):
if not app_auth_token and not app_auth_code:
raise Exception("Both app_auth_code and app_auth_token are None !!!")
self._app_auth_token = app_auth_token
self._app_auth_code = app_auth_code
super().__init__(
appid,
app_notify_url,
app_private_key_string=app_private_key_string,
alipay_public_key_string=alipay_public_key_string,
sign_type=sign_type,
debug=debug
)
@property
def app_auth_token(self):
# 没有则换取token
if not self._app_auth_token:
result = self.api_alipay_open_auth_token_app(self._app_auth_code)
self._app_auth_token = result.get("app_auth_token")
if not self._app_auth_token:
msg = "Get auth token by auth code failed: {}"
raise Exception(msg.format(self._app_auth_code))
return self._app_auth_token
def build_body(self, *args, **kwargs):
data = super().build_body(*args, **kwargs)
if self._app_auth_token:
data["app_auth_token"] = self._app_auth_token
if self._verbose:
logger.debug("data to be signed")
logger.debug(data)
return data
def api_alipay_open_auth_token_app(self, refresh_token=None):
"""
response = {
"code": "10000",
"msg": "Success",
"app_auth_token": "201708BB28623ce3d10f4f62875e9ef5cbeebX07",
"app_refresh_token": "201708BB108a270d8bb6409890d16175a04a7X07",
"auth_app_id": "appid",
"expires_in": 31536000,
"re_expires_in": 32140800,
"user_id": "2088xxxxx
}
"""
if refresh_token:
biz_content = {
"grant_type": "refresh_token",
"refresh_token": refresh_token
}
else:
biz_content = {
"grant_type": "authorization_code",
"code": self._app_auth_code
}
data = self.build_body(
"alipay.open.auth.token.app",
biz_content,
)
response_type = "alipay_open_auth_token_app_response"
return self.verified_sync_response(data, response_type)
def api_alipay_open_auth_token_app_query(self):
biz_content = {"app_auth_token": self.app_auth_token}
data = self.build_body(
"alipay.open.auth.token.app.query",
biz_content,
)
response_type = "alipay_open_auth_token_app_query_response"
return self.verified_sync_response(data, response_type)

@ -0,0 +1,10 @@
#!/usr/bin/env python
# coding: utf-8
"""
compat.py
~~~~~~~~~~
"""
from urllib.parse import quote_plus
from urllib.request import urlopen
from base64 import decodebytes, encodebytes

@ -0,0 +1,26 @@
#!/usr/bin/env python
# coding: utf-8
"""
exceptions.py
~~~~~~~~~~
"""
class AliPayException(Exception):
def __init__(self, code, message):
self.__code = code
self.__message = message
def to_unicode(self):
return "AliPayException: code:{}, message:{}".format(self.__code, self.__message)
def __str__(self):
return self.to_unicode()
def __repr__(self):
return self.to_unicode()
class AliPayValidationError(Exception):
pass

@ -0,0 +1,26 @@
import logging
import logging.config
logging.config.dictConfig({
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {
"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
},
},
"handlers": {
"console": {
"level": "DEBUG",
"formatter": "standard",
"class": "logging.StreamHandler",
},
},
"loggers": {
"python-alipay-sdk": {
"handlers": ["console"],
"level": "DEBUG",
}
}
})
logger = logging.getLogger("python-alipay-sdk")

@ -0,0 +1,10 @@
"""
alipay/utils.py
~~~~~~~~~~
"""
class AliPayConfig:
def __init__(self, timeout=15):
self.timeout = timeout

@ -0,0 +1,73 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: NinEveN
# date: 2021/3/18
# pip install alipay-sdk-python==3.3.398
# !/usr/bin/env python
# -*- coding: utf-8 -*-
from api.utils.alipay import AliPay
from api.utils.alipay.utils import AliPayConfig
from fir_ser.settings import PAY_CONFIG
from datetime import datetime, timedelta
from api.utils.storage.caches import update_order_info
import json
import logging
logger = logging.getLogger(__file__)
class Alipay(object):
def __init__(self):
self.ali_config = PAY_CONFIG.get("ALI")
self.alipay = self.__get_ali_pay()
def __get_ali_pay(self):
return AliPay(
appid=self.ali_config.get("APP_ID"),
app_notify_url=self.ali_config.get("APP_NOTIFY_URL"),
app_private_key_string=self.ali_config.get("APP_PRIVATE_KEY"),
alipay_public_key_string=self.ali_config.get("ALI_PUBLIC_KEY"),
sign_type="RSA2", # RSA 或者 RSA2
debug=False, # 默认False
verbose=True, # 输出调试数据
config=AliPayConfig(timeout=15) # 可选, 请求超时时间
)
def get_pay_pc_url(self, out_trade_no, total_amount, passback_params):
time_expire = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S")
order_string = self.alipay.api_alipay_trade_page_pay(
out_trade_no=out_trade_no,
total_amount=total_amount,
subject=self.ali_config.get("SUBJECT"),
body="充值 %s" % total_amount,
time_expire=time_expire,
return_url=self.ali_config.get("RETURN_URL"),
notify_url=self.ali_config.get("APP_NOTIFY_URL"),
passback_params=json.dumps(passback_params)
)
return "https://openapi.alipay.com/gateway.do?%s" % order_string
def valid_order(self, data):
signature = data.pop("sign")
success = self.alipay.verify(data, signature)
if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):
logger.info("付款成功,等待下一步验证 %s" % data)
app_id = data.get("app_id", "")
if app_id == self.ali_config.get("APP_ID"):
out_trade_no = data.get("out_trade_no", "") # 服务器订单号
passback_params = data.get("passback_params", "")
if passback_params:
ext_parms = json.loads(passback_params)
user_id = ext_parms.get("user_id")
payment_number = data.get("trade_no", "")
return update_order_info(user_id, out_trade_no, payment_number, 1)
else:
logger.error("passback_params %s user_id not exists" % passback_params)
else:
logger.error("APP_ID 校验失败 response: %s server: %s" % (app_id, self.ali_config.get("APP_ID")))
return False

@ -6,7 +6,7 @@
from django.core.cache import cache
from api.models import Apps, UserInfo, AppReleaseInfo, AppUDID, APPToDeveloper, APPSuperSignUsedInfo, \
UserCertificationInfo
UserCertificationInfo, Order
import time, os
from django.utils import timezone
from fir_ser.settings import CACHE_KEY_TEMPLATE, SERVER_DOMAIN, SYNC_CACHE_TO_DATABASE, DEFAULT_MOBILEPROVISION, \
@ -391,3 +391,40 @@ def user_auth_success(user_id):
get_user_free_download_times(user_id, 'get')
get_user_free_download_times(user_id, 'set', USER_FREE_DOWNLOAD_TIMES - AUTH_USER_FREE_DOWNLOAD_TIMES)
return enable_user_download(user_id)
def update_order_info(user_id, out_trade_no, payment_number, payment_type):
with cache.lock("%s_%s" % ('user_order_', out_trade_no)):
try:
user_obj = UserInfo.objects.filter(pk=user_id).first()
order_obj = Order.objects.filter(account=user_obj, order_number=out_trade_no).first()
if order_obj:
if order_obj.status == 1:
download_times = order_obj.actual_download_times + order_obj.actual_download_gift_times
try:
order_obj.status = 0
order_obj.payment_type = payment_type
order_obj.order_type = 0
order_obj.payment_number = payment_number
now = timezone.now()
if not timezone.is_naive(now):
now = timezone.make_naive(now, timezone.utc)
order_obj.pay_time = now
order_obj.description = "充值成功,充值下载次数 %s ,现总共可用次数 %s" % (
download_times, user_obj.download_times)
order_obj.save()
add_user_download_times(user_id, download_times)
logger.info("%s 订单 %s msg:%s" % (user_obj, out_trade_no, order_obj.description))
return True
except Exception as e:
logger.error("%s 订单 %s 更新失败 Exception:%s" % (user_obj, out_trade_no, e))
elif order_obj.status == 0:
return True
else:
return False
else:
logger.error("%s 订单 %s 订单获取失败,或订单已经支付" % (user_obj, out_trade_no))
except Exception as e:
logger.error("%s download_times less then 0. Exception:%s" % (user_obj, e))
return False

@ -18,6 +18,7 @@ from rest_framework.pagination import PageNumberPagination
import logging
from fir_ser.settings import SERVER_DOMAIN
from api.utils.utils import is_valid_domain, delete_local_files, delete_app_screenshots_files
from api.base_views import app_delete
logger = logging.getLogger(__name__)
@ -121,31 +122,7 @@ class AppInfoView(APIView):
res = BaseResponse()
if app_id:
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj:
count = APPToDeveloper.objects.filter(app_id=apps_obj).count()
if apps_obj.issupersign or count > 0:
logger.info("app_id:%s is supersign ,delete this app need clean IOS developer" % (app_id))
IosUtils.clean_app_by_user_obj(apps_obj, request.user)
storage = Storage(request.user)
has_combo = apps_obj.has_combo
if has_combo:
logger.info(
"app_id:%s has_combo ,delete this app need uncombo and clean del_cache_response_by_short" % (
app_id))
apps_obj.has_combo.has_combo = None
del_cache_response_by_short(apps_obj.app_id)
del_cache_by_delete_app(apps_obj.app_id)
for appreleaseobj in AppReleaseInfo.objects.filter(app_id=apps_obj).all():
logger.info("delete app_id:%s need clean all release,release_id:%s" % (
app_id, appreleaseobj.release_id))
storage.delete_file(appreleaseobj.release_id, appreleaseobj.release_type)
delete_local_files(appreleaseobj.release_id, appreleaseobj.release_type)
storage.delete_file(appreleaseobj.icon_url)
appreleaseobj.delete()
delete_app_screenshots_files(storage, apps_obj)
apps_obj.delete()
res = app_delete(apps_obj)
return Response(res.dict)
def put(self, request, app_id):

@ -15,6 +15,9 @@ from api.utils.utils import get_order_num, get_choices_dict
from api.utils.storage.caches import add_user_download_times
import logging
from django.utils import timezone
from api.utils.pay.ali import Alipay
from fir_ser.settings import PAY_SUCCESS_URL
from django.http import HttpResponseRedirect
logger = logging.getLogger(__name__)
@ -56,10 +59,15 @@ class OrderView(APIView):
price_obj = Price.objects.filter(name=price_id).first()
if price_obj:
try:
Order.objects.create(payment_type=0, order_number=get_order_num(),
account=request.user, status=1, order_type=0, actual_amount=price_obj.price,
order_number = get_order_num()
actual_amount = price_obj.price
Order.objects.create(payment_type=0, order_number=order_number,
account=request.user, status=1, order_type=0, actual_amount=actual_amount,
actual_download_times=price_obj.package_size,
actual_download_gift_times=price_obj.download_count_gift)
alipay = Alipay()
pay_url = alipay.get_pay_pc_url(order_number, actual_amount / 100, {'user_id': request.user.id})
res.data = pay_url
return Response(res.dict)
except Exception as e:
logger.error("%s 订单 %s 保存失败 Exception:%s" % (request.user, price_id, e))
@ -122,3 +130,19 @@ class PriceView(APIView):
def put(self, request, price_id):
res = BaseResponse()
return Response(res.dict)
class PaySuccess(APIView):
# authentication_classes = [ExpiringTokenAuthentication]
def get(self, request):
return HttpResponseRedirect(PAY_SUCCESS_URL)
def post(self, request):
alipay = Alipay()
msg = 'failure'
data = request.data.copy().dict()
logger.info("支付回调参数:%s" % data)
if alipay.valid_order(request.data.copy().dict()):
msg = 'success'
return Response(msg)

@ -433,3 +433,23 @@ LOGGING = {
},
},
}
PAY_SUCCESS_URL = 'https://app.hehelucky.cn/user/orders' # 前端页面,支付成功跳转页面
PAY_CONFIG = {
'ALI': {
'APP_ID': "2021002132612737",
'APP_PRIVATE_KEY': '''-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAhRuycGP8sHsZ2gpdEqdrP2iHOMgRYRtw4duqOEpEjnVxUoYYwIKIhITacVItKLrBAHVVdXYBqm89/5BpKGHhLfUUWEabMuRneIiLxjWCdJi4+oHBtv8+E7OIjXwK6X2ahKD/c90XLrllt0Gl9GZmyPVrWRq/WiOO2nmriHPc8zp87/hffKOyW9feJoO71J47Up26VR9MLvPPc8h/QVmhMLDhyOvLFsEvFVeax2vWGpsK+dmWevzquF/Vn6ndzImOWR2jJSipKCMasZvz2TKc03BafIob2uDtbxX0FPhKpO6z0sPh3cNzpzhAmS/ZoyGOV18wJIWq1IuBMq/Q5n4UcwIDAQABAoIBADQmpuHr+tv2Tymjd9XQLG/ad2hi0pRWWQLUurt1NakPEIhBq775JZ2uI5vUk4bqrKWOUx5DTuHE1eikXt8Igl4sMH1ppHLrFDMgZIsS+frOv2K+pfQZyuuTIsQ0Pl4+7ORb49o0XFndH6IOIYRA/rJrnVR660/YsKaelvtOUdolllcBlDQDNDeZ7GurGea7GZW9MFIC9zcc1VTlJ42cs2NMEUDmBh2INd0aNH6ybNQS/+UT/eI4BpBZYaG0s3yEmFr8ZmvlImxanQeQXmZT4xkVziOsDfrSc2NM5TCj4nAAAMC8aAHJlwkKZ6j+gTYtx5SlHWiR5+u/DSsAgfbiMyECgYEAw/4oCuHmd/RTQrYoI7lHP7Jlwnw6dXD/45EbIVDlGYm9yPHR3QTqJb6grCUD3JRd0LRiEQSveed73Ggh8Q0sWhs20t9i1jnOfyJS+xr0PzOjJJ3rpmLVxZr4oCCUdiOa0JYdtTMSDVi8kiIsodtbHCY0rsJH8ztYS1vWFbL7hQMCgYEArdyrD3yGpmbHYEp3b0qOyCwXFiJXEq5RwTdtbL9AKtnug+sjZDBYUE1aIBYtwSs8D7qBiBeUDOw1eoHViheRZfn1WxJ2JoE1Vn7j75f8XrYTWOO2M3BSjwfYnBeiGh3xNPlCndMQyNV7kJnJTM9upwx/1pSlddHcIhF1HT4Of9ECgYAK+O6a9VymuIn0wSfsIBJKEZ26zqOjMYlR3yzKp7G7xUdXuZoLKpxFMq/iE0xtC+1YotCerUl5pKj9hOLpkNg7zyw5kAIDhkb2PSCyKCcmZqiqgyDPNtdK8csbg9dr6cBgDxdoroxDLQWZlMo04YfvQoBOjFfk2RyvU1vf6R5FqwKBgEigFRSy/8wiwsYGVT2390zGnh4w2g6DosMDVEJI4ZUE1A1m+7GuQDXLGgqtOQ+n777iOZmPv9hmEzDJa1nz3liqwUL5w0DyWEV5W92Jr3IgvJQ1CrcSBGqa7HDHrn8aYteuB5XFxQ0foC4XD292dtJw9jW8giFlOH9Cq5k7gvMBAoGARD+jq0/cDVN02dLu5/jjj7dag2J7H+Eissc+4x56KJ1f/WolQ1gAfdGzno0zBKMbIm/t+4W8Bp3NcX01XFeS05iMuNnSqgS9Qe23knK4zblYFmJYTBQzwhr+OgXIx4lP3QJN6Hc+IPWTknjTX/w+HFSihNop971avdncHwEAHs4=
-----END RSA PRIVATE KEY-----''',
'ALI_PUBLIC_KEY': '''-----BEGIN CERTIFICATE-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Fbo1uH7TRBQUizDFKDSHTqlVCtsRCQuIoD0sqKUOaZ4F1mL9Wz97g//QKkDmQdT6VoDUkEghnxBuZhcMCcEGGEFHICVFfcgzbQQI6ebm58TEQVt/tSIO++FXq23Yglkkd6J8fnBicZfZbTfTysaZemjTvY5+3Nyg5Jp2o3OH1oCp1Xj148laVUrFfzxPaiYyZkyf7Rcd6EdmpZDHqchmB0E1FGK/hi8VnmS9KLtTU2/bIMZeD7Mz9N/6iQPhZImaKzbDr76KSfnNdggbkrD57uhU8tMWZ2QDLYdElCFijJlTPGzVRUqxhG7Wk5JW4cfm0CPNvpBe7xvFnfeCqT6fwIDAQAB
-----END CERTIFICATE-----''',
'APP_NOTIFY_URL': 'https://app.hehelucky.cn/api/v1/fir/server/pay_success', # 支付支付回调URL
# 'RETURN_URL': 'https://app.hehelucky.cn/api/v1/fir/server/pay_success', # 支付前端页面回调URL
'RETURN_URL': PAY_SUCCESS_URL, # 支付前端页面回调URL
'SUBJECT': '向 FLY分发平台 充值',
},
'WX': {
}
}

Loading…
Cancel
Save