近期在项目的开发过程当中,须要用到ping++的退款功能,因为使用的版本比官方提供的要低2个小版本,所以问题并非很大。可是因为官方文档有些内容写的比较含蓄,所以遇到了一些问题。
咱们能够经过以下的方式来获取SDK的版本:python
>>> import pingpp >>> pingpp.VERSION '2.0.7' >>> pingpp.api_version '2.0.7'
在官方文档得说明中,咱们能够看到这样3句代码:git
import pingpp pingpp.api_key = 'sk_test_ibbTe5jLGCi5rzfH4OqPW9KC' ch = pingpp.Charge.retrieve('CH-ID') re = ch.refunds.create(description='desc', amount=1)
在这里,咱们看到咱们先导入pingpp库,而后经过赋值的方式将其传入。而后咱们经过pingpp得Charge类的retrieve方法获取给定ch_id,而后再根据其refunds属性得create方法传入关键字参数来实现退款的操做。
若是咱们没有传入api_key,将获得1个AuthenticationError权限错误:github
AuthenticationError: No API key provided. (HINT: set your API key using "pingpp.api_key = <API-KEY>"). You can generate API keys from the Ping++ web interface. See https://pingxx.com for details, or email support@pingxx.com if you have a ny questions.
在这里,我按照官方提供的方式进行赋值,直接就出现上面的状况了,让人比较纳闷。web
下面咱们来看下其实现的源码,在resource模块下的Charge类继承自3个类,分别为CreateableAPIResource, ListableAPIResource,UpdateableAPIResource。其源码以下:api
class Charge(CreateableAPIResource, ListableAPIResource, UpdateableAPIResource): def refund(self, **params): ...
而这3个父类继承自APIResource类,而APIResource类继承自PingppObject类,它是1个Pingpp对象。ide
class CreateableAPIResource(APIResource): @classmethod def create(cls, api_key=None, **params): requestor = api_requestor.APIRequestor(api_key) url = cls.class_url() response, api_key = requestor.request('post', url, params) return convert_to_pingpp_object(response, api_key) class APIResource(PingppObject): @classmethod def retrieve(cls, id, api_key=None, **params): instance = cls(id, api_key, **params) instance.refresh() return instance def refresh(self): self.refresh_from(self.request('get', self.instance_url())) return self @classmethod def class_name(cls): ... return str(urllib.quote_plus(cls.__name__.lower())) @classmethod def class_url(cls): cls_name = cls.class_name() return "/v1/%ss" % (cls_name,) def instance_url(self): ... extn = urllib.quote_plus(id) return "%s/%s" % (base, extn)
从上述代码,咱们能够发现,咱们还能够直接将api_key
以关键字参数的形式传入到retrieve方法中。
咱们调用Charge类的retrieve方法时,其会生成1个实例,而后调用该实例得refresh方法。然后调研该实例的refresh_from方法,使用get请求,而地址为该实例的url。
所以,最终的url地址为https://api.pingxx.com/v1/charges/
,首先class_name
方法返回Charge类名的小写名称,然后在class_url
方法中进行组装后返回给instance_url
方法。
而在调用request方法的过程当中,咱们会涉及到1个convert_to_pingpp_object
方法,其将响应的内容转换为pingpp对象。
经过这种方式咱们完成了官方文档中查询Charge对象的操做,即进行以下的操做:函数
GET https://api.pingxx.com/v1/charges/{CHARGE_ID}
所以,上面ch最终的结果为咱们使用API调用后获得的JSON数据结果,然后咱们经过ch的refunds属性获得这样1个对象:post
... "refunds": { "url": "/v1/charges/ch_xxx/refunds", "has_more": false, "object": "list", "data": [ { ... } ] }
而这个转换的过程是在refresh_from
函数中进行的:ui
def refresh_from(self, values, api_key=None, partial=False): ... for k, v in values.iteritems(): super(PingppObject, self).__setitem__( k, convert_to_pingpp_object(v, api_key))
然后咱们经过object属性获取到ch.refunds的结果为list。经过以下的方式咱们获得的ch.refunds为1个ListObject:url
def convert_to_pingpp_object(resp,api_key): klass_name = resp.get('object') if isinstance(klass_name, basestring): klass = types.get(klass_name, PingppObject)
这样,咱们在create方法中传入的参数与API文档中建立Refund对象的参数一一对应了。而这些传入的参数将在调用api_requestor模块中得APIRequestor类时传入。其中,url为refund对象中的url属性,即上面的/v1/charges/ch_xxx/refunds
。
所以,第3行中的关键字参数description和amount正好对应官方文档中的说明。须要提示的是,description参数只能是最大255个unicode字符,否则又会出现一些问题。
其实ping++的SDK是与其API接口对应的,若是你在使用SDK的过程当中对其传入的参数不明确,能够查看API文档相应篇章中的说明。否则你会遇到ping++平台给你返回一些让你摸不着头脑的回复。
原文:
http://yuki-onna.github.io/jump-out-of-the-refund-of-ping++/
参考文章:
https://www.pingxx.com/api#api-r-new
https://www.pingxx.com/guidance/server/charge/refund
https://github.com/PingPlusPlus/pingpp-python/blob/2.0.7/example/refund.py