Ability to attach metadata to a refund seems inconsistent

136 views
Skip to first unread message

Greg Schafer

unread,
Oct 29, 2015, 6:48:50 PM10/29/15
to Stripe API Discussion
Hey all,

I'm using the python bindings (stripe==1.27.1), python 3.5, and stripe API version '2015-10-16' and am experiencing a relatively minor thing, but wondered if anyone knew why the API behaves this way or if it could be updated to be consistent.

The issue is that calling charge.refund (POSTs to /v1/charges/<id>/refund) and providing metadata causes an API error, but calling charge.refunds.create (POSTs to /v1/charges/<id>/refunds) and providing metadata works fine.

iPython log/demonstration included below. I added some print statements to the stripe-python library to print out the inputs and outputs of the request (code location) and included that info for the 2 requests (it appears immediately under In [3] and In [4]):

In [2]: chg = stripe.Charge.retrieve('ch_170b6R2w01qINp7nDby9kBZG')
In [3]: chg.refund(amount=1000, metadata={'testing': True})
method post
headers {'Authorization': 'Bearer <removed>', 'X-Stripe-Client-User-Agent': '{"lang": "python", "uname": "Linux gregserver 3.13.0-43-generic #72-Ubuntu SMP Mon Dec 8 19:35:06 UTC 2014 x86_64 x86_64", "lang_version": "3.5.0", "publisher": "stripe", "httplib": "requests", "bindings_version": "1.27.1", "platform": "Linux-3.13.0-43-generic-x86_64-with-debian-jessie-sid"}', 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'Stripe/v1 PythonBindings/1.27.1', 'Stripe-Version': '2015-10-16'}
post_data amount=1000&metadata%5Btesting%5D=True
rbody b'{\n  "error": {\n    "type": "invalid_request_error",\n    "message": "Received unknown parameter: metadata",\n    "param": "metadata"\n  }\n}\n'
rcode 400
rheaders {'request-id': 'req_7F5kcMGoINV6bL', 'content-length': '137', 'access-control-max-age': '300', 'connection': 'keep-alive', 'stripe-version': '2015-10-16', 'content-type': 'application/json', 'date': 'Tue, 27 Oct 2015 17:48:55 GMT', 'server': 'nginx', 'cache-control': 'no-cache, no-store', 'access-control-allow-origin': '*', 'access-control-allow-credentials': 'true', 'access-control-allow-methods': 'GET, POST, HEAD, OPTIONS, DELETE'} 
---------------------------------------------------------------------------
InvalidRequestError                       Traceback (most recent call last)
<ipython-input-3-8287bf5ed037> in <module>()
----> 1 chg.refund(amount=1000, metadata={'testing': True})
/usr/local/lib/python3.5/site-packages/stripe/resource.py in refund(self, idempotency_key, **params)
    513         url = self.instance_url() + '/refund'
    514         headers = populate_headers(idempotency_key)
--> 515         self.refresh_from(self.request('post', url, params, headers))
    516         return self
    517
/usr/local/lib/python3.5/site-packages/stripe/resource.py in request(self, method, url, params, headers)
    205             key=self.api_key, api_base=self.api_base(),
    206             account=self.stripe_account)
--> 207         response, api_key = requestor.request(method, url, params, headers)
    208
    209         return convert_to_stripe_object(response, api_key, self.stripe_account)
/usr/local/lib/python3.5/site-packages/stripe/api_requestor.py in request(self, method, url, params, headers)
    139         rbody, rcode, rheaders, my_api_key = self.request_raw(
    140             method.lower(), url, params, headers)
--> 141         resp = self.interpret_response(rbody, rcode, rheaders)
    142         return resp, my_api_key
    143
/usr/local/lib/python3.5/site-packages/stripe/api_requestor.py in interpret_response(self, rbody, rcode, rheaders)
    270                 rbody, rcode, rheaders)
    271         if not (200 <= rcode < 300):
--> 272             self.handle_api_error(rbody, rcode, resp, rheaders)
    273         return resp
    274
/usr/local/lib/python3.5/site-packages/stripe/api_requestor.py in handle_api_error(self, rbody, rcode, resp, rheaders)
    154             raise error.InvalidRequestError(
    155                 err.get('message'), err.get('param'),
--> 156                 rbody, rcode, resp, rheaders)
    157         elif rcode == 401:
    158             raise error.AuthenticationError(
InvalidRequestError: Request req_7F5ZeBMXn4dFqx: Received unknown parameter: metadata
In [4]: chg.refunds.create(amount=1000, metadata={'testing': True})
method post
headers {'Authorization': 'Bearer <removed>', 'X-Stripe-Client-User-Agent': '{"lang": "python", "uname": "Linux gregserver 3.13.0-43-generic #72-Ubuntu SMP Mon Dec 8 19:35:06 UTC 2014 x86_64 x86_64", "lang_version": "3.5.0", "publisher": "stripe", "httplib": "requests", "bindings_version": "1.27.1", "platform": "Linux-3.13.0-43-generic-x86_64-with-debian-jessie-sid"}', 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'Stripe/v1 PythonBindings/1.27.1', 'Stripe-Version': '2015-10-16'}
post_data amount=1000&metadata%5Btesting%5D=True
rbody b'{\n  "id": "re_170bZC2w01qINp7nHWNWxpqT",\n  "object": "refund",\n  "amount": 1000,\n  "balance_transaction": "txn_170bZC2w01qINp7nB3096v0U",\n  "charge": "ch_170b6R2w01qINp7nDby9kBZG",\n  "created": 1445968142,\n  "currency": "usd",\n  "metadata": {\n    "testing": "True"\n  },\n  "reason": null,\n  "receipt_number": null\n}\n'
rcode 200
rheaders {'request-id': 'req_7F5kWwpqiy6eR5', 'content-length': '315', 'date': 'Tue, 27 Oct 2015 17:49:02 GMT', 'access-control-max-age': '300', 'connection': 'keep-alive', 'stripe-version': '2015-10-16', 'content-type': 'application/json', 'strict-transport-security': 'max-age=31556926; includeSubDomains', 'server': 'nginx', 'cache-control': 'no-cache, no-store', 'access-control-allow-origin': '*', 'access-control-allow-credentials': 'true', 'access-control-allow-methods': 'GET, POST, HEAD, OPTIONS, DELETE'} 
Out[4]:
<Refund refund id=re_170bOd2w01qINp7nn5KX4B3P at 0x7f0bc7f11778> JSON: {
  "amount": 1000,
  "balance_transaction": "txn_170bOd2w01qINp7nWIbQSex1",
  "charge": "ch_170b6R2w01qINp7nDby9kBZG",
  "created": 1445967487,
  "currency": "usd",
  "id": "re_170bOd2w01qINp7nn5KX4B3P",
  "metadata": {
    "testing": "True"
  },
  "object": "refund",
  "reason": null,
  "receipt_number": null
}

The requests look the same to me, other than the URL being hit. I would expect to be able to use the same parameters for both endpoints -- why does one allow metadata and the other doesn't?

Thanks!
Greg 

Matthew Arkin

unread,
Oct 29, 2015, 6:59:49 PM10/29/15
to Jake K.
Prior to June '14, a charge could only have refund (hence POST /charges/chargeId/refund), I'd assume that since there could only be once refund metadata was less useful).

In June 2014, charges where now able to have multiple refunds, promoting refunds to a "first-class API resource", adding the /refunds endpoint beneath a charge and that added the metadata ability, essentially deprecated the old method of single refund per charge. 

I can't say exactly why /refund doesn't support metadata, my guess would be that the preferred method is to use the newer refund endpoints as those are the ones being updated as opposed to the old ones (since it becomes a bit of work to add parameters to older endpoints)

Matt

--
You received this message because you are subscribed to the Google Groups "Stripe API Discussion" group.
To post to this group, send email to api-d...@lists.stripe.com.
Visit this group at http://groups.google.com/a/lists.stripe.com/group/api-discuss/.

To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss...@lists.stripe.com.

Greg Schafer

unread,
Nov 2, 2015, 10:42:49 AM11/2/15
to Stripe API Discussion
Gotcha, thanks for the history perspective/knowledge! I'll stick to /charges/<id>/refunds (charge.refunds.create in python bindings).
Reply all
Reply to author
Forward
0 new messages