parent
d9b0d3718c
commit
9f21a2f51e
@ -1,46 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
""" |
||||
阿里巴巴旗下相关服务 |
||||
|
||||
蚂蚁金服 |
||||
|
||||
阿里云 |
||||
|
||||
因阿里云权限访问控制, 可能每种服务都会映射到一个访问key和秘钥(当然也可以用最大权限) |
||||
|
||||
""" |
||||
|
||||
import logging |
||||
import requests |
||||
|
||||
logger = logging.getLogger(__name__) |
||||
|
||||
|
||||
class BaseAli(object): |
||||
API_BASE_URL = "" |
||||
|
||||
_http = requests.session() |
||||
|
||||
def __init__(self, timeout, auto_retry=False): |
||||
self.timeout = timeout |
||||
self.auto_retry = auto_retry |
||||
|
||||
def request(self, *args, **kwargs): |
||||
raise NotImplementedError() |
||||
|
||||
def get(self, url, **kwargs): |
||||
return self.request( |
||||
method="get", |
||||
url_or_endpoint=url, |
||||
**kwargs |
||||
) |
||||
|
||||
def post(self, url, **kwargs): |
||||
return self.request( |
||||
method="post", |
||||
url_or_endpoint=url, |
||||
**kwargs |
||||
) |
@ -1,58 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
""" |
||||
阿里 api 调用出口 |
||||
|
||||
封装更易用的api |
||||
""" |
||||
|
||||
from django.conf import settings |
||||
from django.utils.functional import LazyObject |
||||
|
||||
from .pay.basic import AliPay |
||||
from .yun.basic import AliYunClient |
||||
|
||||
|
||||
class AliApi(object): |
||||
# 默认的支付配置 |
||||
DEFAULT_ALI_PAY_CONFIG = settings.THIRD_PART_CONFIG["ALI_PAY"]["default"] |
||||
# 指定的支付配置(业务拓展) |
||||
SPECIFIC_ALI_PAY_CONFIG = settings.THIRD_PART_CONFIG["ALI_PAY"].get("pay") or DEFAULT_ALI_PAY_CONFIG |
||||
# 注册云配置 |
||||
ALI_YUN_CONFIG = settings.THIRD_PART_CONFIG["ALI_YUN"] |
||||
|
||||
def __init__(self, ): |
||||
# 支付类业务 |
||||
self.pay = AliPay( |
||||
app_id=self.SPECIFIC_ALI_PAY_CONFIG["app_id"], |
||||
app_private_key_path=self.SPECIFIC_ALI_PAY_CONFIG["app_private_key_path"], |
||||
ali_public_key_path=self.SPECIFIC_ALI_PAY_CONFIG["alipay_public_key_path"], |
||||
notify_url=self.SPECIFIC_ALI_PAY_CONFIG["callback_url"], |
||||
return_url=self.SPECIFIC_ALI_PAY_CONFIG["callback_url"], |
||||
debug=self.SPECIFIC_ALI_PAY_CONFIG["debug"] |
||||
) |
||||
# 转账类业务 |
||||
self.transfer = AliPay( |
||||
app_id=self.DEFAULT_ALI_PAY_CONFIG["app_id"], |
||||
app_private_key_path=self.DEFAULT_ALI_PAY_CONFIG["app_private_key_path"], |
||||
ali_public_key_path=self.DEFAULT_ALI_PAY_CONFIG["alipay_public_key_path"], |
||||
notify_url=self.SPECIFIC_ALI_PAY_CONFIG["callback_url"], |
||||
return_url=self.SPECIFIC_ALI_PAY_CONFIG["callback_url"], |
||||
debug=self.DEFAULT_ALI_PAY_CONFIG["debug"] |
||||
) |
||||
# 阿里云业务 |
||||
self.yun = AliYunClient( |
||||
app_id=self.ALI_YUN_CONFIG.get("ACCESS_KEY"), |
||||
secret=self.ALI_YUN_CONFIG.get("SECRET") |
||||
) |
||||
|
||||
|
||||
class DefaultApi(LazyObject): |
||||
|
||||
def _setup(self): |
||||
self._wrapped = AliApi() |
||||
|
||||
|
||||
ali_api = DefaultApi() |
@ -1,3 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
@ -1,3 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
@ -1,13 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
""" |
||||
实名认证相关接口实现, 实名认证三步走 |
||||
|
||||
认证初始化 > 开始认证 > 认证查询 |
||||
""" |
||||
|
||||
|
||||
class AliCertification(object): |
||||
pass |
@ -1,3 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
@ -1,3 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
@ -1,10 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
|
||||
from .page import AliPage |
||||
from .wap import AliWap |
||||
from .app import AliApp |
||||
from .transfer import AliTransfer |
||||
from .order import AliOrder |
@ -1,82 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
|
||||
""" |
||||
App支付 |
||||
""" |
||||
|
||||
import logging |
||||
|
||||
from .base import BaseAliPayAPI |
||||
|
||||
logger = logging.getLogger(__name__) |
||||
|
||||
|
||||
class AliApp(BaseAliPayAPI): |
||||
|
||||
def direct(self, subject, out_trade_no, total_amount, notify_url=None, return_url=None, **kwargs): |
||||
"""App快速下单接口 |
||||
|
||||
文档DOC地址: https://docs.open.alipay.com/203/105288/ |
||||
|
||||
Parameters |
||||
---------- |
||||
|
||||
subject : string |
||||
订单标题 |
||||
|
||||
out_trade_no : string |
||||
商户订单号唯一凭证 |
||||
|
||||
total_amount : string or float or int |
||||
支付金额,单位元,精确到分 |
||||
|
||||
notify_url: string |
||||
|
||||
支付宝服务器主动通知商户服务器里指定的页面http/https路径 |
||||
|
||||
example: http://www.alipay.com/pay |
||||
|
||||
return_url: string |
||||
|
||||
同步返回地址,HTTP/HTTPS开头字符串 |
||||
|
||||
kwargs : dict |
||||
以下均为可选参数 |
||||
|
||||
body : string |
||||
订单描述 |
||||
|
||||
goods_detail : json |
||||
订单包含的商品列表信息,Json格式: {"show_url":"https://或http://打头的商品的展示地址"} |
||||
在支付时,可点击商品名称跳转到该地址 |
||||
|
||||
passback_params : string |
||||
公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。 |
||||
支付宝只会在异步通知时将该参数原样返回。 |
||||
本参数必须进行UrlEncode之后才可以发送给支付宝 |
||||
|
||||
goods_type : int |
||||
商品主类型:0; 虚拟类商品,1; 实物类商品(默认) |
||||
|
||||
extend_params : |
||||
业务扩张参数(主要用于接入花呗分期) |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
biz_content = { |
||||
"subject": subject, |
||||
"out_trade_no": out_trade_no, |
||||
"total_amount": total_amount, |
||||
"product_code": "QUICK_MSECURITY_PAY", # 销售产品码,目前仅支持这个类型 |
||||
# "qr_pay_mode":4 |
||||
} |
||||
|
||||
biz_content.update(**kwargs) |
||||
|
||||
return self._generate_url("alipay.trade.app.pay", biz_content, notify_url, return_url) |
@ -1,31 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
|
||||
class BaseAliPayAPI(object): |
||||
""" |
||||
|
||||
支付宝支付API基类 |
||||
|
||||
""" |
||||
|
||||
def __init__(self, client=None): |
||||
self._client = client |
||||
|
||||
def _get(self, url, **kwargs): |
||||
if hasattr(self, "API_BASE_URL"): |
||||
kwargs['api_base_url'] = getattr(self, "API_BASE_URL") |
||||
return self._client.get(url, **kwargs) |
||||
|
||||
def _post(self, url, **kwargs): |
||||
if hasattr(self, "API_BASE_URL"): |
||||
kwargs['api_base_url'] = getattr(self, "API_BASE_URL") |
||||
return self._client.post(url, **kwargs) |
||||
|
||||
def _generate_url(self, method, *args, **kwargs): |
||||
return self._client.generate_url(method, *args, **kwargs) |
||||
|
||||
@property |
||||
def app_id(self): |
||||
return self._client.app_id |
@ -1,44 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
""" |
||||
统一收单相关服务 |
||||
""" |
||||
|
||||
import logging |
||||
|
||||
from .base import BaseAliPayAPI |
||||
|
||||
logger = logging.getLogger(__name__) |
||||
|
||||
|
||||
class AliOrder(BaseAliPayAPI): |
||||
|
||||
def query(self, out_biz_no=None, trade_no=None, **kwargs): |
||||
"""统一收单线下交易查询. |
||||
|
||||
Parameters |
||||
---------- |
||||
out_biz_no : string |
||||
订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 |
||||
trade_no,out_trade_no如果同时存在优先取trade_no |
||||
|
||||
trade_no : string |
||||
支付宝交易号,和商户订单号不能同时为空 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
biz_content = { |
||||
"out_biz_no": out_biz_no, |
||||
"trade_no": trade_no |
||||
} |
||||
|
||||
biz_content.update(kwargs) |
||||
|
||||
url = self._generate_url("alipay.trade.query", biz_content) |
||||
|
||||
return self._get(url) |
@ -1,81 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
""" |
||||
PC支付场景 |
||||
""" |
||||
|
||||
import logging |
||||
|
||||
from .base import BaseAliPayAPI |
||||
|
||||
logger = logging.getLogger(__name__) |
||||
|
||||
|
||||
class AliPage(BaseAliPayAPI): |
||||
|
||||
def direct(self, subject, out_trade_no, total_amount, notify_url=None, return_url=None, **kwargs): |
||||
"""PC快速下单接口 |
||||
|
||||
文档DOC地址: https://docs.open.alipay.com/270/alipay.trade.page.pay |
||||
|
||||
Parameters |
||||
---------- |
||||
|
||||
subject : string |
||||
订单标题 |
||||
|
||||
out_trade_no : string |
||||
商户订单号唯一凭证 |
||||
|
||||
total_amount : string or float or int |
||||
支付金额,单位元,精确到分 |
||||
|
||||
notify_url: string |
||||
|
||||
支付宝服务器主动通知商户服务器里指定的页面http/https路径 |
||||
|
||||
example: http://www.alipay.com/pay |
||||
|
||||
return_url: string |
||||
|
||||
同步返回地址,HTTP/HTTPS开头字符串 |
||||
|
||||
kwargs : dict |
||||
以下均为可选参数 |
||||
|
||||
body : string |
||||
订单描述 |
||||
|
||||
goods_detail : json |
||||
订单包含的商品列表信息,Json格式: {"show_url":"https://或http://打头的商品的展示地址"} |
||||
在支付时,可点击商品名称跳转到该地址 |
||||
|
||||
passback_params : string |
||||
公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。 |
||||
支付宝只会在异步通知时将该参数原样返回。 |
||||
本参数必须进行UrlEncode之后才可以发送给支付宝 |
||||
|
||||
goods_type : int |
||||
商品主类型:0; 虚拟类商品,1; 实物类商品(默认) |
||||
|
||||
extend_params : |
||||
业务扩张参数(主要用于接入花呗分期) |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
biz_content = { |
||||
"subject": subject, |
||||
"out_trade_no": out_trade_no, |
||||
"total_amount": total_amount, |
||||
"product_code": "FAST_INSTANT_TRADE_PAY", # 销售产品码,目前仅支持这个类型 |
||||
# "qr_pay_mode":4 |
||||
} |
||||
|
||||
biz_content.update(**kwargs) |
||||
|
||||
return self._generate_url("alipay.trade.page.pay", biz_content, notify_url, return_url) |
@ -1,100 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
""" |
||||
转账支付 `api` |
||||
""" |
||||
|
||||
import logging |
||||
|
||||
from .base import BaseAliPayAPI |
||||
|
||||
logger = logging.getLogger(__name__) |
||||
|
||||
|
||||
class AliTransfer(BaseAliPayAPI): |
||||
|
||||
def transfer(self, out_biz_no, payee_account, amount, payee_real_name, is_verify=True, **kwargs): |
||||
"""转账接口 |
||||
|
||||
在构建完成数据结构体之前进行验证码校验 |
||||
|
||||
该方法加入了 业务代码, 业务若有变动, 请根据实际需求进行更改 |
||||
|
||||
Parameters |
||||
---------- |
||||
out_biz_no : string |
||||
商户转账唯一凭证 |
||||
|
||||
payee_account : string |
||||
收款方账户 |
||||
|
||||
amount : string or int or float |
||||
转账金额(单位: 元, 保留俩位小数, 最小转帐金额 0.1元) |
||||
|
||||
payee_real_name: string |
||||
收款方真实姓名 |
||||
|
||||
is_verify: bool, default: True |
||||
是否进行验证码校验 |
||||
|
||||
kwargs : dict |
||||
以下均为可选参数 |
||||
|
||||
payer_show_name : string |
||||
付款方姓名 |
||||
|
||||
remark : string |
||||
转账备注(支持200个英文/100个汉字)。当付款方为企业账户,且转账金额达到(大于等于)50000元,remark不能为空。 |
||||
收款方可见,会展示在收款用户的收支详情中。 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
# 如果需要校验, 获取验证码进行校验, 校验不通过直接响应校验不通过 |
||||
if is_verify: |
||||
kwargs.pop("verify_code", None) |
||||
|
||||
biz_content = { |
||||
"out_biz_no": out_biz_no, |
||||
"payee_account": payee_account, |
||||
"payee_type": "ALIPAY_LOGONID", # 收款方账户类型 |
||||
"amount": amount, |
||||
"payee_real_name": payee_real_name |
||||
} |
||||
|
||||
biz_content.update(**kwargs) |
||||
|
||||
url = self._generate_url("alipay.fund.trans.toaccount.transfer", biz_content) |
||||
|
||||
return self._get(url) |
||||
|
||||
def query(self, out_biz_no, order_id, **kwargs): |
||||
"""转账交易查询接口. |
||||
|
||||
Parameters |
||||
---------- |
||||
out_biz_no : string |
||||
商户转账唯一凭证 |
||||
|
||||
order_id : string |
||||
支付宝商户转账唯一凭证 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
biz_content = { |
||||
"out_biz_no": out_biz_no, |
||||
"order_id": order_id |
||||
} |
||||
|
||||
biz_content.update(kwargs) |
||||
|
||||
url = self._generate_url("alipay.fund.trans.order.query", biz_content) |
||||
|
||||
return self._get(url) |
@ -1,81 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
""" |
||||
手机网站支付 |
||||
""" |
||||
|
||||
import logging |
||||
|
||||
from .base import BaseAliPayAPI |
||||
|
||||
logger = logging.getLogger(__name__) |
||||
|
||||
|
||||
class AliWap(BaseAliPayAPI): |
||||
|
||||
def direct(self, subject, out_trade_no, total_amount, notify_url=None, return_url=None, **kwargs): |
||||
"""WAP快速下单接口 |
||||
|
||||
文档DOC地址: https://docs.open.alipay.com/203/105288/ |
||||
|
||||
Parameters |
||||
---------- |
||||
|
||||
subject : string |
||||
订单标题 |
||||
|
||||
out_trade_no : string |
||||
商户订单号唯一凭证 |
||||
|
||||
total_amount : string or float or int |
||||
支付金额,单位元,精确到分 |
||||
|
||||
notify_url: string |
||||
|
||||
支付宝服务器主动通知商户服务器里指定的页面http/https路径 |
||||
|
||||
example: http://www.alipay.com/pay |
||||
|
||||
return_url: string |
||||
|
||||
同步返回地址,HTTP/HTTPS开头字符串 |
||||
|
||||
kwargs : dict |
||||
以下均为可选参数 |
||||
|
||||
body : string |
||||
订单描述 |
||||
|
||||
goods_detail : json |
||||
订单包含的商品列表信息,Json格式: {"show_url":"https://或http://打头的商品的展示地址"} |
||||
在支付时,可点击商品名称跳转到该地址 |
||||
|
||||
passback_params : string |
||||
公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。 |
||||
支付宝只会在异步通知时将该参数原样返回。 |
||||
本参数必须进行UrlEncode之后才可以发送给支付宝 |
||||
|
||||
goods_type : int |
||||
商品主类型:0; 虚拟类商品,1; 实物类商品(默认) |
||||
|
||||
extend_params : |
||||
业务扩张参数(主要用于接入花呗分期) |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
biz_content = { |
||||
"subject": subject, |
||||
"out_trade_no": out_trade_no, |
||||
"total_amount": total_amount, |
||||
"product_code": "QUICK_WAP_WAY", # 销售产品码,目前仅支持这个类型 |
||||
# "qr_pay_mode":4 |
||||
} |
||||
|
||||
biz_content.update(**kwargs) |
||||
|
||||
return self._generate_url("alipay.trade.wap.pay", biz_content, notify_url, return_url) |
@ -1,198 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
import json |
||||
import inspect |
||||
|
||||
from urllib.parse import quote_plus |
||||
|
||||
from Crypto.PublicKey import RSA |
||||
|
||||
from django.utils.timezone import now |
||||
|
||||
from .. import BaseAli |
||||
from ..tools import calculate_signature |
||||
from .api.base import BaseAliPayAPI |
||||
from . import api |
||||
|
||||
|
||||
def _is_api_endpoint(instance): |
||||
return issubclass(instance.__class__, BaseAliPayAPI) |
||||
|
||||
|
||||
class AliPay(BaseAli): |
||||
# 在构造函数中初始化 |
||||
API_BASE_URL = "" |
||||
|
||||
# PC支付 |
||||
pc = api.AliPage() |
||||
# Wap支付 |
||||
wap = api.AliWap() |
||||
# App支付 |
||||
app = api.AliApp() |
||||
# 转账 |
||||
transfer = api.AliTransfer() |
||||
# 统一收单相关 |
||||
order = api.AliOrder() |
||||
|
||||
def __init__( |
||||
self, app_id, app_private_key_path, ali_public_key_path, |
||||
notify_url, return_url, debug, |
||||
): |
||||
|
||||
# 如果是调试模式,则用调试api |
||||
if debug: |
||||
setattr(self, "API_BASE_URL", "https://openapi.alipaydev.com/gateway.do") |
||||
else: |
||||
setattr(self, "API_BASE_URL", "https://openapi.alipay.com/gateway.do") |
||||
|
||||
# 暂时用不到父类方法参数(设置默认值) |
||||
super(AliPay, self).__init__(timeout=3, auto_retry=True) |
||||
|
||||
self.app_id = app_id |
||||
self.notify_url = notify_url |
||||
self.return_url = return_url |
||||
|
||||
# 加载应用的私钥 |
||||
with open(app_private_key_path) as fp: |
||||
self.app_private_key = RSA.importKey(fp.read()) |
||||
|
||||
# 加载支付宝公钥 |
||||
with open(ali_public_key_path) as fp: |
||||
self.ali_public_key = RSA.importKey(fp.read()) |
||||
|
||||
def __new__(cls, *args, **kwargs): |
||||
self = super(AliPay, cls).__new__(cls) |
||||
api_endpoints = inspect.getmembers(self, _is_api_endpoint) |
||||
for name, api_ins in api_endpoints: |
||||
api_cls = type(api_ins) |
||||
api_ins = api_cls(self) |
||||
setattr(self, name, api_ins) |
||||
return self |
||||
|
||||
def request(self, method, url_or_endpoint, **kwargs): |
||||
if not url_or_endpoint.startswith(('http://', 'https://')): |
||||
api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL) |
||||
url = '{base}{endpoint}'.format( |
||||
base=api_base_url, |
||||
endpoint=url_or_endpoint |
||||
) |
||||
else: |
||||
url = url_or_endpoint |
||||
|
||||
res = self._http.request( |
||||
method=method, |
||||
url=url, |
||||
**kwargs |
||||
) |
||||
|
||||
res.raise_for_status() |
||||
|
||||
return self._handle_result(res) |
||||
|
||||
def _handle_result(self, res): |
||||
"""解析请求结果并校验签名 |
||||
|
||||
""" |
||||
return res.json() |
||||
|
||||
def get_url_params(self, data): |
||||
data.pop("sign", None) |
||||
# 排序后[(k, v), ...] |
||||
ordered_items = sorted( |
||||
((k, v if not isinstance(v, dict) else json.dumps(v, separators=(',', ':'))) |
||||
for k, v in data.items()) |
||||
) |
||||
# 拼接成待签名的字符串 |
||||
unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in ordered_items) |
||||
# 对上一步得到的字符串进行签名 |
||||
sign = calculate_signature(unsigned_string.encode("utf-8"), self.app_private_key) |
||||
# 处理URL |
||||
quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in ordered_items) |
||||
# 添加签名,获得最终的订单信息字符串 |
||||
signed_string = quoted_string + "&sign=" + quote_plus(sign) |
||||
return signed_string |
||||
|
||||
def build_body(self, method, biz_content, notify_url=None, return_url=None): |
||||
"""构建请求体 |
||||
|
||||
公有请求体 + 业务请求体 |
||||
|
||||
Parameters |
||||
---------- |
||||
method : string |
||||
请求API |
||||
|
||||
biz_content: dict |
||||
业务参数 |
||||
|
||||
notify_url: string, default: None |
||||
异步通知地址 |
||||
|
||||
return_url: string, default: None |
||||
同步地址 |
||||
|
||||
Returns |
||||
------- |
||||
请求参数: dict |
||||
""" |
||||
data = { |
||||
"app_id": self.app_id, |
||||
"method": method, |
||||
"charset": "utf-8", |
||||
"sign_type": "RSA2", |
||||
"timestamp": now().strftime("%Y-%m-%d %H:%M:%S"), |
||||
"version": "1.0", |
||||
"biz_content": biz_content |
||||
} |
||||
|
||||
# 同步回调 |
||||
if notify_url is not None: |
||||
data["notify_url"] = notify_url |
||||
|
||||
# 异步回调 |
||||
if return_url is not None: |
||||
data["return_url"] = return_url |
||||
|
||||
return data |
||||
|
||||
def generate_url(self, method, biz_content, notify_url=None, return_url=None): |
||||
"""构建请求体. |
||||
公有请求体 + 业务请求体 |
||||
|
||||
Parameters |
||||
---------- |
||||
method : string |
||||
|
||||
请求API |
||||
|
||||
biz_content: dict |
||||
|
||||
业务参数 |
||||
|
||||
notify_url: string, default: None |
||||
|
||||
异步通知 `url` |
||||
|
||||
return_url: string, default: None |
||||
|
||||
同步通知 `url` |
||||
|
||||
|
||||
Returns |
||||
------- |
||||
string |
||||
""" |
||||
|
||||
if notify_url is None: |
||||
notify_url = self.notify_url |
||||
|
||||
if return_url is None: |
||||
return_url = self.return_url |
||||
|
||||
body = self.build_body(method, biz_content, notify_url, return_url) |
||||
|
||||
params = self.get_url_params(body) |
||||
|
||||
return "{}?{}".format(self.API_BASE_URL, params) |
@ -1,69 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/8/2 |
||||
|
||||
import json |
||||
import time |
||||
import uuid |
||||
|
||||
from Crypto.Signature import PKCS1_v1_5 |
||||
from Crypto.Hash import SHA256 |
||||
|
||||
from base64 import encodebytes |
||||
from base64 import decodebytes |
||||
|
||||
|
||||
def format_params(params): |
||||
"""格式化参数 |
||||
|
||||
""" |
||||
return sorted( |
||||
((k, v if not isinstance(v, dict) else json.dumps(v, separators=(',', ':'))) for k, v in params.items()) |
||||
) |
||||
|
||||
|
||||
def verify_signature(params, api_key): |
||||
"""验证签名 |
||||
|
||||
""" |
||||
|
||||
sign = params.pop("sign", "") |
||||
|
||||
if "sign_type" in params: |
||||
params.pop("sign_type") |
||||
|
||||
# 针对字符串进行排序 |
||||
ordered_items = format_params(params) |
||||
message = "&".join(u"{}={}".format(k, v) for k, v in ordered_items) |
||||
# 验签 |
||||
signer = PKCS1_v1_5.new(api_key) |
||||
digest = SHA256.new() |
||||
digest.update(message.encode("utf8")) |
||||
if signer.verify(digest, decodebytes(sign.encode("utf8"))): |
||||
return True |
||||
return False |
||||
|
||||
|
||||
def calculate_signature(params, api_key): |
||||
"""计算签名 |
||||
|
||||
""" |
||||
signer = PKCS1_v1_5.new(api_key) |
||||
signature = signer.sign(SHA256.new(params)) |
||||
# base64 编码,转换为unicode表示并移除回车 |
||||
sign = encodebytes(signature).decode("utf8").replace("\n", "") |
||||
return sign |
||||
|
||||
|
||||
def get_uuid(): |
||||
"""获取 `UUID` |
||||
|
||||
""" |
||||
return str(uuid.uuid4()) |
||||
|
||||
|
||||
def get_iso_8061_date(): |
||||
"""获取 `iso` 标准时间 |
||||
|
||||
""" |
||||
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) |
@ -1,12 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/9/18 |
||||
|
||||
""" |
||||
阿里云相关服务, 目前提供支持: |
||||
|
||||
点播服务 |
||||
|
||||
短信服务 |
||||
|
||||
""" |
@ -1,7 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/9/18 |
||||
|
||||
|
||||
from .vod import AliYunVod |
||||
from .sms import AliYunSms |
@ -1,41 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/9/18 |
||||
|
||||
|
||||
class BaseAliYunAPI(object): |
||||
""" |
||||
|
||||
阿里云服务API基类 |
||||
|
||||
""" |
||||
|
||||
def __init__(self, client=None): |
||||
self._client = client |
||||
|
||||
def _get(self, action, **kwargs): |
||||
if hasattr(self, "API_BASE_URL"): |
||||
kwargs['api_base_url'] = getattr(self, "API_BASE_URL") |
||||
|
||||
if hasattr(self, "VERSION"): |
||||
kwargs["version"] = getattr(self, "VERSION") |
||||
|
||||
return self._client.get(action, **kwargs) |
||||
|
||||
def _post(self, action, **kwargs): |
||||
|
||||
if hasattr(self, "API_BASE_URL"): |
||||
kwargs['api_base_url'] = getattr(self, "API_BASE_URL") |
||||
|
||||
if hasattr(self, "VERSION"): |
||||
kwargs["version"] = getattr(self, "VERSION") |
||||
|
||||
return self._client.post(action, **kwargs) |
||||
|
||||
@property |
||||
def app_id(self): |
||||
return self._client.app_id |
||||
|
||||
@property |
||||
def secret_key(self): |
||||
return self._client.secret_key |
@ -1,144 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/9/18 |
||||
|
||||
""" |
||||
短信服务 |
||||
""" |
||||
|
||||
from .base import BaseAliYunAPI |
||||
|
||||
|
||||
class AliYunSms(BaseAliYunAPI): |
||||
VERSION = "2017-05-25" |
||||
|
||||
API_BASE_URL = "http://dysmsapi.aliyuncs.com/" |
||||
|
||||
def send(self, phone_numbers, sign_name, template_code, template_param, **kwargs): |
||||
"""发送短信 |
||||
|
||||
详情参见: |
||||
https://help.aliyun.com/document_detail/55284.html?spm=5176.8195934.1001856.3.a36a4183xdfqaD |
||||
|
||||
Parameters |
||||
---------- |
||||
phone_numbers : string |
||||
短信接收号码,支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码. |
||||
发送国际/港澳台消息时,接收号码格式为00+国际区号+号码,如“0085200000000” |
||||
|
||||
sign_name: string |
||||
短信签名 |
||||
|
||||
template_code: string |
||||
短信模板ID,发送国际/港澳台消息时,请使用国际/港澳台短信模版 |
||||
|
||||
template_param: json |
||||
短信模板变量替换JSON串,友情提示:如果JSON中需要带换行符,请参照标准的JSON协议 |
||||
|
||||
kwargs: dict |
||||
可选字段: |
||||
|
||||
SmsUpExtendCode |
||||
上行短信扩展码,无特殊需要此字段的用户请忽略此字段 |
||||
|
||||
OutId |
||||
外部流水扩展字段 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
data = { |
||||
"PhoneNumbers": phone_numbers, |
||||
"SignName": sign_name, |
||||
"TemplateCode": template_code, |
||||
"TemplateParam": template_param |
||||
} |
||||
|
||||
data.update(**kwargs) |
||||
|
||||
return self._get(action="SendSms", data=data) |
||||
|
||||
def send_batch( |
||||
self, |
||||
phone_number_json, |
||||
sign_name_json, |
||||
template_code, |
||||
template_param_json, |
||||
sms_upextend_code_json=None, |
||||
): |
||||
"""短信批量发送 |
||||
|
||||
详情参见: |
||||
https://help.aliyun.com/document_detail/66041.html?spm=a2c4g.11186623.6.565.290415e8yJ7E0N |
||||
|
||||
Parameters |
||||
---------- |
||||
phone_number_json : string |
||||
短信接收号码,JSON格式,批量上限为100个手机号码, |
||||
批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式 |
||||
|
||||
sign_name_json: string |
||||
短信签名,JSON格式 |
||||
|
||||
template_code: int |
||||
短信模板ID |
||||
|
||||
template_param_json: int |
||||
短信模板变量替换JSON串,友情提示:如果JSON中需要带换行符,请参照标准的JSON协议。 |
||||
|
||||
sms_upextend_code_json: string |
||||
上行短信扩展码,JSON格式,无特殊需要此字段的用户请忽略此字段 |
||||
|
||||
""" |
||||
data = { |
||||
"PhoneNumberJson": phone_number_json, |
||||
"SignNameJson": sign_name_json, |
||||
"TemplateCode": template_code, |
||||
"TemplateParamJson": template_param_json |
||||
} |
||||
|
||||
if sms_upextend_code_json is not None: |
||||
data["SmsUpExtendCodeJson"] = sms_upextend_code_json |
||||
|
||||
return self._get(action="SendBatchSms", data=data) |
||||
|
||||
def query(self, phone_number, send_date, page_size, current_page, biz_id=None): |
||||
"""短信查询 |
||||
|
||||
详情参见: |
||||
https://help.aliyun.com/document_detail/55289.html?spm=a2c4g.11186623.6.563.56d7577756pHT4 |
||||
|
||||
Parameters |
||||
---------- |
||||
phone_number : string |
||||
短信接收号码,如果需要查询国际短信,号码前需要带上对应国家的区号,区号的获取详见国际短信支持国家信息查询API接口 |
||||
|
||||
send_date: string |
||||
短信发送日期格式yyyyMMdd,支持最近30天记录查询 |
||||
|
||||
page_size: int |
||||
页大小Max=50 |
||||
|
||||
current_page: int |
||||
当前页码 |
||||
|
||||
biz_id: string OR None |
||||
可选, 发送流水号,从调用发送接口返回值中获取 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
data = { |
||||
"PhoneNumber": phone_number, |
||||
"SendDate": send_date, |
||||
"PageSize": page_size, |
||||
"CurrentPage": current_page |
||||
} |
||||
|
||||
if biz_id is not None: |
||||
data["BizId"] = biz_id |
||||
|
||||
return self._get(action="QuerySendDetails", data=data) |
@ -1,212 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/9/18 |
||||
|
||||
""" |
||||
点播服务 |
||||
""" |
||||
|
||||
from .base import BaseAliYunAPI |
||||
|
||||
|
||||
class AliYunVod(BaseAliYunAPI): |
||||
VERSION = "2017-03-21" |
||||
|
||||
API_BASE_URL = "http://vod.cn-shanghai.aliyuncs.com/" |
||||
|
||||
def get_play_auth(self, video_id, timeout): |
||||
"""获取播放 `auth` |
||||
|
||||
Parameters |
||||
---------- |
||||
video_id : string |
||||
|
||||
视频 `vid` |
||||
|
||||
timeout: string |
||||
|
||||
获取到的播放 `key` 的有效时间 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
data = { |
||||
"VideoId": video_id, |
||||
"AuthInfoTimeout": timeout |
||||
} |
||||
|
||||
return self._get(action="GetVideoPlayAuth", data=data) |
||||
|
||||
def get_play_info( |
||||
self, video_id, formats=None, timeout=None, stream_type=None, definition=None, |
||||
result_type=None, output_type=None, |
||||
): |
||||
"""获取播放 `auth` |
||||
|
||||
Parameters |
||||
---------- |
||||
video_id : string |
||||
|
||||
视频 `vid` |
||||
|
||||
formats: string |
||||
视频流格式,多个用逗号分隔。支持格式:mp4 m3u8 mp3 |
||||
默认获取所有格式的流 |
||||
|
||||
timeout: string |
||||
播放地址过期时间,单位:秒。 |
||||
最小值为1(未指定时,以URL鉴权中用户设置的默认有效时长为准),最大值无限制。 |
||||
只有开启了 URL鉴权 才会生效。 |
||||
|
||||
stream_type: string |
||||
视频流类型,多个用逗号分隔。支持类型:video audio |
||||
默认获取所有类型的流。 |
||||
|
||||
definition: string |
||||
视频流清晰度,多个用逗号分隔。取值范围:FD(流畅)LD(标清)SD(高清)HD(超清)OD(原画)2K(2K)4K(4K) |
||||
默认获取所有清晰度的流。 |
||||
|
||||
result_type: string |
||||
返回数据类型。取值范围 : |
||||
Single(每种清晰度和格式只返回一路最新转码完成的流) |
||||
Multiple(每种清晰度和格式返回所有转码完成的流) |
||||
默认为Single类型。 |
||||
|
||||
output_type: string |
||||
输出地址类型。取值范围 : |
||||
oss(回源地址) |
||||
cdn(加速地址) |
||||
默认为cdn类型。注意:仅支持播放格式为mp4的oss地址 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
data = { |
||||
"VideoId": video_id, |
||||
"Formats": formats, |
||||
"AuthTimeout": timeout, |
||||
"StreamType": stream_type, |
||||
"Definition": definition, |
||||
"ResultType": result_type, |
||||
"OutputType": output_type |
||||
} |
||||
|
||||
return self._get(action="GetPlayInfo", data=data) |
||||
|
||||
def query_flow_data( |
||||
self, domain_name=None, start_time=None, end_time=None, time_merge=None, interval=None, |
||||
location_name_en=None, isp_name_en=None, |
||||
): |
||||
"""查询流量数据 |
||||
|
||||
获取加速域名的网络流量监控数据,单位:byte。 |
||||
不指定StartTime和EndTime时,默认读取过去24小时的数据。也可以按指定的起止时间查询。 |
||||
|
||||
注意: |
||||
支持域名批量查询,多个域名用半角逗号分隔。 |
||||
最多可获取近90天的数据。 |
||||
|
||||
Parameters |
||||
---------- |
||||
domain_name : string |
||||
要查询的域名。若为空,则默认返回所有加速域名的合并数据。支持批量查询,多个域名用半角逗号分隔。 |
||||
|
||||
start_time: string |
||||
获取数据起始时间点。日期格式按照ISO8601表示法,使用UTC时间。 |
||||
若为空,则默认读取最近24小时的数据。 |
||||
|
||||
end_time: string |
||||
结束时间。需大于起始时间,日期格式按照ISO8601表示法,使用UTC时间。格式为:YYYY-MM-DDThh:mmZ。 |
||||
|
||||
time_merge: string |
||||
时间合并。取值范围: |
||||
on:默认值。每条记录的时间间隔会根据时间跨度进行合并。 |
||||
off:返回5分钟粒度数据,最大时间跨度为31天。 |
||||
|
||||
interval: string |
||||
查询数据的时间粒度。单位:秒,取值范围:300 3600 14400 28800 86400 |
||||
默认值:300 |
||||
|
||||
location_name_en: string |
||||
区域英文名。默认为所有区域。 |
||||
|
||||
isp_name_en: string |
||||
运营商英文名。默认为所有运营商。 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
data = { |
||||
"DomainName": domain_name, |
||||
"StartTime": start_time, |
||||
"EndTime": end_time, |
||||
"TimeMerge": time_merge, |
||||
"Interval": interval, |
||||
"LocationNameEn": location_name_en, |
||||
"IspNameEn": isp_name_en |
||||
} |
||||
|
||||
return self._get(action="DescribeDomainFlowData", data=data) |
||||
|
||||
def query_bps_data( |
||||
self, domain_name=None, start_time=None, end_time=None, time_merge=None, interval=None, |
||||
location_name_en=None, isp_name_en=None, |
||||
): |
||||
"""查询网络带宽 |
||||
|
||||
获取加速域名的网络带宽监控数据,单位:bit/second。 |
||||
不指定StartTime和EndTime时,默认读取过去24小时的数据。也可以按指定的起止时间查询。 |
||||
|
||||
注意: |
||||
支持域名批量查询,多个域名用半角逗号分隔。 |
||||
最多可获取近90天的数据。 |
||||
|
||||
Parameters |
||||
---------- |
||||
domain_name : string |
||||
要查询的域名。若为空,则默认返回所有加速域名的合并数据。支持批量查询,多个域名用半角逗号分隔。 |
||||
|
||||
start_time: string |
||||
获取数据起始时间点。日期格式按照ISO8601表示法,使用UTC时间。 |
||||
若为空,则默认读取最近24小时的数据。 |
||||
|
||||
end_time: string |
||||
结束时间。需大于起始时间,日期格式按照ISO8601表示法,使用UTC时间。格式为:YYYY-MM-DDThh:mmZ。 |
||||
|
||||
time_merge: string |
||||
时间合并。取值范围: |
||||
on:默认值。每条记录的时间间隔会根据时间跨度进行合并。 |
||||
off:返回5分钟粒度数据,最大时间跨度为31天。 |
||||
|
||||
interval: string |
||||
查询数据的时间粒度。单位:秒,取值范围:300 3600 14400 28800 86400 |
||||
默认值:300 |
||||
|
||||
location_name_en: string |
||||
区域英文名。默认为所有区域。 |
||||
|
||||
isp_name_en: string |
||||
运营商英文名。默认为所有运营商。 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
|
||||
data = { |
||||
"DomainName": domain_name, |
||||
"StartTime": start_time, |
||||
"EndTime": end_time, |
||||
"TimeMerge": time_merge, |
||||
"Interval": interval, |
||||
"LocationNameEn": location_name_en, |
||||
"IspNameEn": isp_name_en |
||||
} |
||||
|
||||
return self._get(action="DescribeDomainBpsData", data=data) |
@ -1,224 +0,0 @@ |
||||
#! /usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
# Date: 2018/9/18 |
||||
|
||||
import hmac |
||||
import base64 |
||||
import inspect |
||||
import logging |
||||
|
||||
from urllib.parse import quote |
||||
|
||||
from .. import BaseAli |
||||
from ..yun import api |
||||
from ..yun.api.base import BaseAliYunAPI |
||||
from ..tools import get_uuid |
||||
from ..tools import get_iso_8061_date |
||||
|
||||
logger = logging.getLogger(__name__) |
||||
|
||||
QUERY_STRING = None |
||||
|
||||
|
||||
def _get_signature(string_to_sign, secret, signer=hmac): |
||||
"""获取签名 |
||||
|
||||
""" |
||||
secret = "{}&".format(secret) |
||||
hmb = signer.new(secret.encode("utf-8"), string_to_sign.encode("utf-8"), "sha1").digest() |
||||
return quote(base64.standard_b64encode(hmb).decode("ascii"), safe="~") |
||||
|
||||
|
||||
def __pop_standard_urlencode(query): |
||||
ret = "" |
||||
for item in query: |
||||
|
||||
if not item[1]: |
||||
continue |
||||
|
||||
ret += quote(item[0], safe="~") + "=" + quote(item[1], safe="~") + "&" |
||||
|
||||
return ret |
||||
|
||||
|
||||
def _compose_sign_string(query_params): |
||||
"""组成签名字符串 |
||||
|
||||
""" |
||||
|
||||
sorted_parameters = sorted(query_params.items(), key=lambda url_params: url_params[0]) |
||||
|
||||
string_to_sign = __pop_standard_urlencode(sorted_parameters) |
||||
|
||||
query_string = string_to_sign[:-1] |
||||
|
||||
global QUERY_STRING |
||||
QUERY_STRING = query_string |
||||
|
||||
tosign = "GET&%2F&" + quote(query_string, safe="~") |
||||
|
||||
return tosign |
||||
|
||||
|
||||
def _is_api_endpoint(instance): |
||||
return issubclass(instance.__class__, BaseAliYunAPI) |
||||
|
||||
|
||||
class AliYunClient(BaseAli): |
||||
VERSION = "" # 每个接口都需要版本号 |
||||
|
||||
API_BASE_URL = "" # 阿里云api网关地址均从使用的接口定义 |
||||
|
||||
# 点播api |
||||
vod = api.AliYunVod() |
||||
# 短信api |
||||
sms = api.AliYunSms() |
||||
|
||||
def __new__(cls, *args, **kwargs): |
||||
"""注册接口 |
||||
|
||||
""" |
||||
self = super(AliYunClient, cls).__new__(cls) |
||||
api_endpoints = inspect.getmembers(self, _is_api_endpoint) |
||||
for name, api_ins in api_endpoints: |
||||
api_cls = type(api_ins) |
||||
api_ins = api_cls(self) |
||||
setattr(self, name, api_ins) |
||||
return self |
||||
|
||||
def __init__(self, app_id, secret, timeout=None, auto_retry=False): |
||||
super(AliYunClient, self).__init__( |
||||
timeout, auto_retry |
||||
) |
||||
self.app_id = app_id |
||||
self.secret = secret |
||||
|
||||
def request(self, method, action, **kwargs): |
||||
"""构造请求用户授权的url |
||||
|
||||
Parameters |
||||
---------- |
||||
method : string |
||||
请求类型 |
||||
|
||||
action : string |
||||
API的命名,固定值,如发送短信API的值为:SendSms |
||||
|
||||
kwargs : dict |
||||
请求参数 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
query_string = "" |
||||
|
||||
api_base_url = kwargs.pop("api_base_url", self.API_BASE_URL) |
||||
|
||||
# 获取请求参数 |
||||
if isinstance(kwargs.get("data", {}), dict): |
||||
|
||||
data = kwargs["data"] |
||||
|
||||
data["Action"] = action |
||||
|
||||
# 封装公有请求参数 |
||||
|
||||
# 接口版本号 |
||||
if "Version" not in data: |
||||
data["Version"] = kwargs.pop("version", self.VERSION) |
||||
|
||||
# 时间戳 |
||||
data["Timestamp"] = get_iso_8061_date() |
||||
# 签名算法名称 |
||||
data["SignatureMethod"] = "HMAC-SHA1" |
||||
# 签名版本 |
||||
data["SignatureVersion"] = "1.0" |
||||
# 随机码 |
||||
data["SignatureNonce"] = get_uuid() |
||||
# access key |
||||
data["AccessKeyId"] = self.app_id |
||||
# 接口响应类型均为 `JSON` |
||||
data["Format"] = "JSON" |
||||
|
||||
# 计算签名 |
||||
string_to_sign = _compose_sign_string(data) |
||||
signature = _get_signature(string_to_sign, self.secret) |
||||
|
||||
query_string = '?{}{}'.format(QUERY_STRING, "&" + "Signature=" + signature) |
||||
|
||||
# 结果解析器, 如果需要定制可单独定制 |
||||
result_processor = kwargs.pop("result_processor", None) |
||||
|
||||
url = "{}{}".format(api_base_url, query_string) |
||||
|
||||
res = self._http.request( |
||||
method=method, |
||||
url=url, |
||||
**kwargs |
||||
) |
||||
|
||||
# res.raise_for_status() |
||||
|
||||
return self._handle_result(res, method, url, result_processor, **kwargs) |
||||
|
||||
def _handle_result(self, res, method=None, url=None, |
||||
result_processor=None, **kwargs): |
||||
"""结果解析 |
||||
|
||||
Parameters |
||||
---------- |
||||
res : request instance |
||||
响应对象 response |
||||
|
||||
method : string |
||||
请求方法 |
||||
|
||||
url : string |
||||
请求的 `url` |
||||
|
||||
result_processor: func OR None |
||||
结果处理器 |
||||
|
||||
kwargs: dict |
||||
更多参数 |
||||
|
||||
Returns |
||||
------- |
||||
dict |
||||
""" |
||||
if not isinstance(res, dict): |
||||
result = res.json() |
||||
else: |
||||
result = res |
||||
|
||||
if not isinstance(result, dict): |
||||
return result |
||||
|
||||
if "Code" in result: |
||||
|
||||
code = result.get("Code") |
||||
|
||||
if code != "OK": |
||||
logger.error("AliApi {} 调用异常, Code {} Errmsg {}".format( |
||||
url, code, result.get("Message") |
||||
)) |
||||
|
||||
if code in []: |
||||
self.request(method, url, **kwargs) |
||||
|
||||
return result if not result_processor else result_processor(result) |
||||
|
||||
def get(self, url, **kwargs): |
||||
return self.request( |
||||
method="get", |
||||
action=url, |
||||
**kwargs |
||||
) |
||||
|
||||
def post(self, url, **kwargs): |
||||
return self.request( |
||||
method="post", |
||||
action=url, |
||||
**kwargs |
||||
) |
@ -1,73 +0,0 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding:utf-8 -*- |
||||
# project: 12月 |
||||
# author: NinEveN |
||||
# date: 2019/12/19 |
||||
|
||||
import re |
||||
import os |
||||
import zipfile |
||||
# from biplist import * |
||||
from androguard.core.bytecodes import apk |
||||
import plistlib |
||||
|
||||
|
||||
def get_android_data(package_file): |
||||
try: |
||||
apkobj = apk.APK(package_file) |
||||
except Exception as err: |
||||
print(err) |
||||
else: |
||||
if apkobj.is_valid_APK(): |
||||
versioncode = apkobj.get_androidversion_code() |
||||
bundle_id = apkobj.get_package() |
||||
labelname = apkobj.get_app_name() |
||||
versioname = apkobj.get_androidversion_name() |
||||
sdk_version = apkobj.get_target_sdk_version() |
||||
return labelname, bundle_id, versioncode, versioname, sdk_version |
||||
|
||||
|
||||
def get_ios_data(ios_file): |
||||
if zipfile.is_zipfile(ios_file): |
||||
ipaobj = zipfile.ZipFile(ios_file) |
||||
info_path = get_ios_info_path(ipaobj) |
||||
if info_path: |
||||
plist_data = ipaobj.read(info_path) |
||||
print(plist_data) |
||||
if b"ProvisionedDevices" in plist_data: |
||||
print("adhoc") |
||||
else: |
||||
print("inhouse") |
||||
|
||||
|
||||
def get_ios_info_path(ipaobj): |
||||
infopath_re = re.compile(r'.*.app/embedded.mobileprovision') |
||||
for i in ipaobj.namelist(): |
||||
m = infopath_re.match(i) |
||||
if m is not None: |
||||
return m.group() |
||||
|
||||
|
||||
def get_ios_icon_path(ipaobj): |
||||
infopath_re = re.compile(r'Payload/[^/]*.app/AppIcon[^/]*[^(ipad)].png') |
||||
for i in ipaobj.namelist(): |
||||
m = infopath_re.match(i) |
||||
if m is not None: |
||||
return m.group() |
||||
|
||||
|
||||
def get_package_size(package_path): |
||||
fsize = os.path.getsize(package_path) |
||||
fsize = fsize / float(1024 * 1024) |
||||
return round(fsize, 2) |
||||
|
||||
|
||||
# a=get_ios_data("/root/v1.1.8/Riot.ipa") |
||||
# print(a) |
||||
|
||||
ipaobj = zipfile.ZipFile("M:\\Breeze.ipa") |
||||
info_path = get_ios_icon_path(ipaobj) |
||||
print(info_path) |
||||
|
||||
with open('111222.png', 'wb') as f: |
||||
f.write(ipaobj.read(info_path)) |
@ -1,48 +0,0 @@ |
||||
from django.conf import settings |
||||
from api.utils.geetest import GeeTestLib |
||||
|
||||
|
||||
def verify(verify_data, uid=None, extend_params=None): |
||||
"""第三方滑动验证码校验. |
||||
|
||||
选用第三方的验证组件, 根据参数进行校验 |
||||
根据布尔值辨别是否校验通过 |
||||
|
||||
Parameters |
||||
---------- |
||||
|
||||
verify_data : dict |
||||
请求数据 |
||||
|
||||
uid: string, default: None |
||||
用户UID, 如果存在就免受滑动验证码的限制 |
||||
|
||||
extend_params : dict |
||||
预留的扩展参数 |
||||
|
||||
Returns |
||||
------- |
||||
True OR False |
||||
""" |
||||
captcha_config = settings.GEE_TEST |
||||
if captcha_config.get("verify_status"): |
||||
|
||||
status = True |
||||
|
||||
if uid in captcha_config.get("not_verify"): |
||||
return True |
||||
|
||||
gt = GeeTestLib(captcha_config["gee_test_access_id"], captcha_config["gee_test_access_key"]) |
||||
challenge = verify_data.get(gt.FN_CHALLENGE, '') |
||||
validate = verify_data.get(gt.FN_VALIDATE, '') |
||||
seccode = verify_data.get(gt.FN_SECCODE, '') |
||||
# status = request.session.get(gt.GT_STATUS_SESSION_KEY, 1) |
||||
# user_id = request.session.get("user_id") |
||||
|
||||
if status: |
||||
result = gt.success_validate(challenge, validate, seccode, None) |
||||
else: |
||||
result = gt.failback_validate(challenge, validate, seccode) |
||||
return True if result else False |
||||
else: |
||||
return True |
@ -1,199 +0,0 @@ |
||||
# !/usr/bin/env python |
||||
# -*- coding:utf-8 -*- |
||||
|
||||
import sys |
||||
import random |
||||
import json |
||||
import requests |
||||
import time |
||||
from hashlib import md5 |
||||
|
||||
if sys.version_info >= (3,): |
||||
xrange = range |
||||
|
||||
VERSION = "3.2.0" |
||||
|
||||
|
||||
class GeeTestLib(object): |
||||
FN_CHALLENGE = "geetest_challenge" |
||||
FN_VALIDATE = "geetest_validate" |
||||
FN_SECCODE = "geetest_seccode" |
||||
|
||||
GT_STATUS_SESSION_KEY = "gt_server_status" |
||||
|
||||
API_URL = "http://api.geetest.com" |
||||
REGISTER_HANDLER = "/register.php" |
||||
VALIDATE_HANDLER = "/validate.php" |
||||
|
||||
def __init__(self, captcha_id, private_key): |
||||
self.private_key = private_key |
||||
self.captcha_id = captcha_id |
||||
self.sdk_version = VERSION |
||||
self._response_str = "" |
||||
|
||||
def pre_process(self, user_id=None): |
||||
""" |
||||
验证初始化预处理. |
||||
""" |
||||
status, challenge = self._register(user_id) |
||||
self._response_str = self._make_response_format(status, challenge) |
||||
return status |
||||
|
||||
def _register(self, user_id=None): |
||||
challenge = self._register_challenge(user_id) |
||||
if len(challenge) == 32: |
||||
challenge = self._md5_encode("".join([challenge, self.private_key])) |
||||
return 1, challenge |
||||
else: |
||||
return 0, self._make_fail_challenge() |
||||
|
||||
def get_response_str(self): |
||||
return self._response_str |
||||
|
||||
def _make_fail_challenge(self): |
||||
rnd1 = random.randint(0, 99) |
||||
rnd2 = random.randint(0, 99) |
||||
md5_str1 = self._md5_encode(str(rnd1)) |
||||
md5_str2 = self._md5_encode(str(rnd2)) |
||||
challenge = md5_str1 + md5_str2[0:2] |
||||
return challenge |
||||
|
||||
def _make_response_format(self, success=1, challenge=None): |
||||
if not challenge: |
||||
challenge = self._make_fail_challenge() |
||||
string_format = json.dumps( |
||||
{'success': success, 'gt': self.captcha_id, 'challenge': challenge}) |
||||
return string_format |
||||
|
||||
def _register_challenge(self, user_id=None): |
||||
if user_id: |
||||
register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}".format( |
||||
api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id) |
||||
else: |
||||
register_url = "{api_url}{handler}?gt={captcha_ID}".format( |
||||
api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id) |
||||
try: |
||||
response = requests.get(register_url, timeout=2) |
||||
if response.status_code == requests.codes.ok: |
||||
res_string = response.text |
||||
else: |
||||
res_string = "" |
||||
except: |
||||
res_string = "" |
||||
return res_string |
||||
|
||||
def success_validate(self, challenge, validate, seccode, user_id=None, gt=None, data='', userinfo=''): |
||||
""" |
||||
正常模式的二次验证方式.向geetest server 请求验证结果. |
||||
""" |
||||
if not self._check_para(challenge, validate, seccode): |
||||
return 0 |
||||
if not self._check_result(challenge, validate): |
||||
return 0 |
||||
validate_url = "{api_url}{handler}".format( |
||||
api_url=self.API_URL, handler=self.VALIDATE_HANDLER) |
||||
query = { |
||||
"seccode": seccode, |
||||
"sdk": ''.join(["python_", self.sdk_version]), |
||||
"user_id": user_id, |
||||
"data": data, |
||||
"timestamp": time.time(), |
||||
"challenge": challenge, |
||||
"userinfo": userinfo, |
||||
"captchaid": gt |
||||
} |
||||
backinfo = self._post_values(validate_url, query) |
||||
if backinfo == self._md5_encode(seccode): |
||||
return 1 |
||||
else: |
||||
return 0 |
||||
|
||||
def _post_values(self, apiserver, data): |
||||
response = requests.post(apiserver, data) |
||||
return response.text |
||||
|
||||
def _check_result(self, origin, validate): |
||||
encodeStr = self._md5_encode(self.private_key + "geetest" + origin) |
||||
if validate == encodeStr: |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
def failback_validate(self, challenge, validate, seccode): |
||||
""" |
||||
failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果. |
||||
""" |
||||
if not self._check_para(challenge, validate, seccode): |
||||
return 0 |
||||
validate_str = validate.split('_') |
||||
encode_ans = validate_str[0] |
||||
encode_fbii = validate_str[1] |
||||
encode_igi = validate_str[2] |
||||
decode_ans = self._decode_response(challenge, encode_ans) |
||||
decode_fbii = self._decode_response(challenge, encode_fbii) |
||||
decode_igi = self._decode_response(challenge, encode_igi) |
||||
validate_result = self._validate_fail_image( |
||||
decode_ans, decode_fbii, decode_igi) |
||||
return validate_result |
||||
|
||||
def _check_para(self, challenge, validate, seccode): |
||||
return (bool(challenge.strip()) and bool(validate.strip()) and bool(seccode.strip())) |
||||
|
||||
def _validate_fail_image(self, ans, full_bg_index, img_grp_index): |
||||
thread = 3 |
||||
full_bg_name = str(self._md5_encode(str(full_bg_index)))[0:10] |
||||
bg_name = str(self._md5_encode(str(img_grp_index)))[10:20] |
||||
answer_decode = "" |
||||
for i in range(0, 9): |
||||
if i % 2 == 0: |
||||
answer_decode += full_bg_name[i] |
||||
elif i % 2 == 1: |
||||
answer_decode += bg_name[i] |
||||
x_decode = answer_decode[4:] |
||||
x_int = int(x_decode, 16) |
||||
result = x_int % 200 |
||||
if result < 40: |
||||
result = 40 |
||||
if abs(ans - result) < thread: |
||||
return 1 |
||||
else: |
||||
return 0 |
||||
|
||||
def _md5_encode(self, values): |
||||
if type(values) == str: |
||||
values = values.encode() |
||||
m = md5(values) |
||||
return m.hexdigest() |
||||
|
||||
def _decode_rand_base(self, challenge): |
||||
str_base = challenge[32:] |
||||
i = 0 |
||||
temp_array = [] |
||||
for i in xrange(len(str_base)): |
||||
temp_char = str_base[i] |
||||
temp_ascii = ord(temp_char) |
||||
result = temp_ascii - 87 if temp_ascii > 57 else temp_ascii - 48 |
||||
temp_array.append(result) |
||||
decode_res = temp_array[0] * 36 + temp_array[1] |
||||
return decode_res |
||||
|
||||
def _decode_response(self, challenge, userresponse): |
||||
if len(userresponse) > 100: |
||||
return 0 |
||||
shuzi = (1, 2, 5, 10, 50) |
||||
chongfu = set() |
||||
key = {} |
||||
count = 0 |
||||
for i in challenge: |
||||
if i in chongfu: |
||||
continue |
||||
else: |
||||
value = shuzi[count % 5] |
||||
chongfu.add(i) |
||||
count += 1 |
||||
key.update({i: value}) |
||||
res = 0 |
||||
for i in userresponse: |
||||
res += key.get(i, 0) |
||||
res = res - self._decode_rand_base(challenge) |
||||
return res |
@ -1,19 +0,0 @@ |
||||
from django.core.cache import cache |
||||
|
||||
import datetime |
||||
|
||||
from rest_framework.authentication import BaseAuthentication |
||||
from rest_framework import exceptions |
||||
from api.models import Token |
||||
|
||||
from rest_framework.permissions import BasePermission |
||||
|
||||
|
||||
class LoginUserPermission(BaseAuthentication): |
||||
|
||||
def has_permission(self, request, view): |
||||
|
||||
if request.user: |
||||
return True |
||||
else: |
||||
return False |
@ -1,36 +0,0 @@ |
||||
from rest_framework.views import APIView |
||||
from api.utils.geetest import GeeTestLib |
||||
from django.conf import settings |
||||
import json |
||||
from rest_framework.response import Response |
||||
|
||||
|
||||
class CaptchaView(APIView): |
||||
def get(self, request): |
||||
gt = GeeTestLib(settings.GEE_TEST["gee_test_access_id"], settings.GEE_TEST["gee_test_access_key"]) |
||||
gt.pre_process() |
||||
# 设置 geetest session, 用于是否启用滑动验证码向 geetest 发起远程验证, 如果取不到的话只是对本地轨迹进行校验 |
||||
# self.request.session[gt.GT_STATUS_SESSION_KEY] = status |
||||
# request.session["user_id"] = user_id |
||||
response_str = gt.get_response_str() |
||||
response_str = json.loads(response_str) |
||||
|
||||
return Response({"error_no": 0, "data": response_str}) |
||||
|
||||
|
||||
from captcha.models import CaptchaStore |
||||
from captcha.helpers import captcha_image_url |
||||
import json |
||||
|
||||
|
||||
class AjaxExampleForm(APIView): |
||||
|
||||
def get(self, request): |
||||
to_json_response = dict() |
||||
to_json_response['status'] = 0 |
||||
to_json_response['new_cptch_key'] = CaptchaStore.generate_key() |
||||
to_json_response['new_cptch_image'] = captcha_image_url(to_json_response['new_cptch_key']) |
||||
CaptchaStore.remove_expired() |
||||
a = CaptchaStore.objects.filter(hashkey=to_json_response['new_cptch_key']).first() |
||||
print(a) |
||||
return Response(to_json_response) |
Loading…
Reference in new issue