Commit 6c50713a authored by Jérome Perrin's avatar Jérome Perrin Committed by Gabriel Monnerat

stripe: support creating checkout session with all dicts and lists serialized

```py
kw = {...}
kw['metadata[key]'] = 'value'
connector.createSession(**kw)
```

but it would be more beautiful to be able to write it like this:

```py
kw = {...}
kw['metadata] = {'key': 'value'}
connector.createSession(**kw)
```

This would need change in the way the request data is serialised in
createSession.
parent f269e6ca
Pipeline #25079 passed with stage
in 0 seconds
...@@ -26,10 +26,7 @@ ...@@ -26,10 +26,7 @@
# #
############################################################################## ##############################################################################
from six.moves import urllib from six.moves import urllib
from copy import deepcopy
import requests import requests
import six import six
...@@ -60,23 +57,16 @@ class StripeConnector(XMLObject): ...@@ -60,23 +57,16 @@ class StripeConnector(XMLObject):
PropertySheet.DublinCore PropertySheet.DublinCore
) )
def buildLineKey(self, prefix, key_list): def serializeSessionParameter(self, request_data, key, value):
for key in key_list: if isinstance(value, list) or isinstance(value, dict):
prefix += "[%s]" % key iterator = six.iteritems(value) if isinstance(value, dict) else enumerate(value)
return prefix for subkey, subvalue in iterator:
self.serializeSessionParameter(
def buildLine(self, data_dict, prefix, key_list, value): request_data,
if not isinstance(value, dict): "{}[{}]".format(key, subkey),
data_dict[self.buildLineKey(prefix, key_list)] = value subvalue)
else: else:
for subkey, subvalue in six.iteritems(value): request_data[key] = value
self.buildLine(data_dict, prefix, key_list + [subkey,], subvalue)
def buildLineItemList(self, prefix, line_item_list):
data_dict = {}
for key, value in enumerate(line_item_list):
self.buildLine(data_dict, prefix, [key,], value)
return data_dict
def createSession(self, data, **kw): def createSession(self, data, **kw):
""" """
...@@ -85,12 +75,6 @@ class StripeConnector(XMLObject): ...@@ -85,12 +75,6 @@ class StripeConnector(XMLObject):
data is a dict, see https://stripe.com/docs/api/checkout/sessions/create data is a dict, see https://stripe.com/docs/api/checkout/sessions/create
""" """
end_point = "checkout/sessions" end_point = "checkout/sessions"
# copy data, not to mutate caller's data
request_data = deepcopy(data)
if "mode" not in request_data:
request_data["mode"] = "payment"
header_dict = { header_dict = {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
} }
...@@ -98,8 +82,13 @@ class StripeConnector(XMLObject): ...@@ -98,8 +82,13 @@ class StripeConnector(XMLObject):
if not url_string.endswith("/"): if not url_string.endswith("/"):
url_string += "/" url_string += "/"
url_string += end_point url_string += end_point
line_item_list = request_data.pop("line_items") request_data = {}
request_data.update(self.buildLineItemList("line_items", line_item_list)) for key, value in six.iteritems(data):
self.serializeSessionParameter(request_data, key, value)
if "mode" not in request_data:
request_data["mode"] = "payment"
response = requests.post( response = requests.post(
url_string, url_string,
headers=header_dict, headers=header_dict,
......
...@@ -45,10 +45,7 @@ class TestStripePaymentSession(ERP5TypeTestCase): ...@@ -45,10 +45,7 @@ class TestStripePaymentSession(ERP5TypeTestCase):
self.connector_reference = "abc" self.connector_reference = "abc"
self.session_url = "https://mock:8080/checkout/sessions" self.session_url = "https://mock:8080/checkout/sessions"
self.data = { self.default_item_line = {
"success_url": "http://success",
"cancel_url": "http://cancel",
"line_items": [{
"price_data": { "price_data": {
"currency": "eur", "currency": "eur",
"unit_amount": "100", "unit_amount": "100",
...@@ -57,7 +54,11 @@ class TestStripePaymentSession(ERP5TypeTestCase): ...@@ -57,7 +54,11 @@ class TestStripePaymentSession(ERP5TypeTestCase):
} }
}, },
"quantity": 1 "quantity": 1
}, { }
self.data = {
"success_url": "http://success",
"cancel_url": "http://cancel",
"line_items": [self.default_item_line, {
"price_data": { "price_data": {
"currency": "eur", "currency": "eur",
"unit_amount": "200", "unit_amount": "200",
...@@ -220,6 +221,157 @@ class TestStripePaymentSession(ERP5TypeTestCase): ...@@ -220,6 +221,157 @@ class TestStripePaymentSession(ERP5TypeTestCase):
response = connector.createSession(data=self.data.copy()) response = connector.createSession(data=self.data.copy())
self.assertEqual(response["id"], "123") self.assertEqual(response["id"], "123")
def test_api_create_session_with_metadata(self):
connector = self._create_connector()
def _request_callback(request):
self.assertEqual(
'application/x-www-form-urlencoded', request.headers['Content-Type'],
request.headers)
body = parse_qs(request.body)
self.assertEqual(
body, {
"success_url": ["http://success"],
"cancel_url": ["http://cancel"],
"line_items[0][price_data][currency]": ["eur"],
"line_items[0][price_data][unit_amount]": ["100"],
"line_items[0][price_data][product_data][name]": ["First Line"],
"line_items[0][quantity]": ["1"],
"metadata[key]": ["value"],
"mode": ["payment"],
})
return (
200, {
'content-type': 'application/json'
},
json.dumps(
{
"id": "123",
"status": "open",
"object": "checkout.session"
}))
with responses.RequestsMock() as rsps:
rsps.add_callback(
responses.POST,
self.session_url,
_request_callback,
)
response = connector.createSession(
data={
"success_url": "http://success",
"cancel_url": "http://cancel",
"line_items": [
self.default_item_line
],
"metadata": {
"key": "value",
},
"mode": "payment"
})
self.assertEqual(response["id"], "123")
def test_api_create_session_with_any_hash(self):
""" Test if dicts and lists are serialized properly
"""
connector = self._create_connector()
def _request_callback(request):
self.assertEqual(
'application/x-www-form-urlencoded', request.headers['Content-Type'],
request.headers)
body = parse_qs(request.body)
self.assertEqual(
body, {
"automatic_tax[enabled]": ['true'],
"automatic_tax[status]": ['complete'],
"success_url": ["http://success"],
"cancel_url": ["http://cancel"],
'custom_text[shipping_text][message]': ['Rue XV'],
'custom_text[submit][message]': ['Rue XV'],
'customer_details[address]': ['Rue XV'],
'customer_details[email]': ['text@text.com'],
'customer_details[name]': ['My name'],
'customer_details[phone]': ['2199909'],
'customer_details[tax_exempt]': ['tax'],
'customer_details[tax_ids][0][type]': ['eu_vat'],
'customer_details[tax_ids][0][value]': ['33'],
'customer_details[tax_ids][1][type]': ['br_cnpj'],
'customer_details[tax_ids][1][value]': ['33'],
"line_items[0][price_data][currency]": ["eur"],
"line_items[0][price_data][unit_amount]": ["100"],
"line_items[0][price_data][product_data][name]": ["First Line"],
"line_items[0][quantity]": ["1"],
"metadata[key]": ["value"],
"metadata[key1]": ["value1"],
"metadata[key2]": ["value2"],
"mode": ["payment"],
"payment_method_options[acss_debit][current]": ['usd']
})
return (
200, {
'content-type': 'application/json'
},
json.dumps(
{
"id": "123",
"status": "open",
"object": "checkout.session"
}))
with responses.RequestsMock() as rsps:
rsps.add_callback(
responses.POST,
self.session_url,
_request_callback,
)
response = connector.createSession(
data={
"success_url": "http://success",
"cancel_url": "http://cancel",
"line_items": [
self.default_item_line
],
"metadata": {
"key": "value",
"key1": "value1",
"key2": "value2",
},
"automatic_tax": {
"enabled": "true",
"status": "complete"
},
"custom_text": {
"shipping_text": {
"message": "Rue XV",
},
"submit": {
"message": "Rue XV",
}
},
"customer_details": {
"address": "Rue XV",
"email": "text@text.com",
"name": "My name",
"phone": "2199909",
"tax_exempt": "tax",
"tax_ids": [{
"type": "eu_vat",
"value": "33"
}, {
"type": "br_cnpj",
"value": "33"
}]
},
"payment_method_options": {
"acss_debit": {
"current": "usd"
}
},
"mode": "payment"
})
self.assertEqual(response["id"], "123")
def test_create_stripe_payment_session_open(self): def test_create_stripe_payment_session_open(self):
connector = self._create_connector() connector = self._create_connector()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment