Utiliser le Checkout API pour la session de souscription (accept_url/cancel_url)
hosted_page_links.payment_info (POST /v1/subscription) n'honore pas accept_url/cancel_url malgré la doc, même passés en query string (constaté en test sur test.colibre.fr). On génère maintenant la page de paiement via POST /v1/session/subscription (Checkout API), qui accepte ces champs dans son body et redirige effectivement le navigateur — même mécanisme déjà fonctionnel pour l'ajout de moyen de paiement (create_recurring_session). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+15
-14
@@ -1,5 +1,4 @@
|
|||||||
import os
|
import os
|
||||||
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
|
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
@@ -62,13 +61,6 @@ def update_customer(handle: str, data: dict) -> dict:
|
|||||||
return _call("PUT", f"/v1/customer/{handle}", json=data)
|
return _call("PUT", f"/v1/customer/{handle}", json=data)
|
||||||
|
|
||||||
|
|
||||||
def _with_query(url: str, **params: str) -> str:
|
|
||||||
parts = urlsplit(url)
|
|
||||||
query = dict(parse_qsl(parts.query))
|
|
||||||
query.update(params)
|
|
||||||
return urlunsplit(parts._replace(query=urlencode(query)))
|
|
||||||
|
|
||||||
|
|
||||||
def create_subscription_session(
|
def create_subscription_session(
|
||||||
plan_handle: str,
|
plan_handle: str,
|
||||||
handle: str,
|
handle: str,
|
||||||
@@ -78,9 +70,6 @@ def create_subscription_session(
|
|||||||
customer_handle: str | None = None,
|
customer_handle: str | None = None,
|
||||||
create_customer: dict | None = None,
|
create_customer: dict | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
# CreateSubscription (POST /v1/subscription) n'a pas de champs accept_url/
|
|
||||||
# cancel_url : ils doivent être ajoutés en query string sur le lien
|
|
||||||
# hosted_page_links.payment_info renvoyé, pas dans le body (cf. doc Frisbii).
|
|
||||||
body: dict = {
|
body: dict = {
|
||||||
"plan": plan_handle,
|
"plan": plan_handle,
|
||||||
"signup_method": "link",
|
"signup_method": "link",
|
||||||
@@ -92,9 +81,21 @@ def create_subscription_session(
|
|||||||
body["create_customer"] = create_customer
|
body["create_customer"] = create_customer
|
||||||
if no_trial:
|
if no_trial:
|
||||||
body["no_trial"] = True
|
body["no_trial"] = True
|
||||||
data = _call("POST", "/v1/subscription", json=body)
|
_call("POST", "/v1/subscription", json=body)
|
||||||
payment_info = data["hosted_page_links"]["payment_info"]
|
# CreateSubscription (POST /v1/subscription) n'a pas de champs accept_url/
|
||||||
return _with_query(payment_info, accept_url=accept_url, cancel_url=cancel_url)
|
# cancel_url. La session de checkout dédiée (Checkout API), elle, les
|
||||||
|
# honore et redirige réellement le navigateur après succès/annulation.
|
||||||
|
data = _call(
|
||||||
|
"POST",
|
||||||
|
"/v1/session/subscription",
|
||||||
|
json={
|
||||||
|
"subscription": handle,
|
||||||
|
"accept_url": accept_url,
|
||||||
|
"cancel_url": cancel_url,
|
||||||
|
},
|
||||||
|
base_url=_checkout_url(),
|
||||||
|
)
|
||||||
|
return data["url"]
|
||||||
|
|
||||||
|
|
||||||
def create_recurring_session(
|
def create_recurring_session(
|
||||||
|
|||||||
@@ -26,14 +26,11 @@ def test_update_customer(fake_httpx):
|
|||||||
|
|
||||||
|
|
||||||
def test_create_subscription_session_with_customer_handle(fake_httpx):
|
def test_create_subscription_session_with_customer_handle(fake_httpx):
|
||||||
|
fake_httpx["queue"].append(fake_httpx["Response"](200, {"handle": "abo-1-1"}))
|
||||||
fake_httpx["queue"].append(
|
fake_httpx["queue"].append(
|
||||||
fake_httpx["Response"](
|
fake_httpx["Response"](
|
||||||
200,
|
200,
|
||||||
{
|
{"id": "cs_1", "url": "https://checkout.reepay.com/#/subscription/cs_1"},
|
||||||
"hosted_page_links": {
|
|
||||||
"payment_info": "https://checkout.reepay.com/#/sub-1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
url = client.create_subscription_session(
|
url = client.create_subscription_session(
|
||||||
@@ -43,33 +40,37 @@ def test_create_subscription_session_with_customer_handle(fake_httpx):
|
|||||||
"https://app/ko",
|
"https://app/ko",
|
||||||
customer_handle="colibre-1",
|
customer_handle="colibre-1",
|
||||||
)
|
)
|
||||||
assert url == (
|
assert url == "https://checkout.reepay.com/#/subscription/cs_1"
|
||||||
"https://checkout.reepay.com/"
|
|
||||||
"?accept_url=https%3A%2F%2Fapp%2Fok&cancel_url=https%3A%2F%2Fapp%2Fko"
|
create_body = fake_httpx["calls"][0]["json"]
|
||||||
"#/sub-1"
|
assert create_body["plan"] == "plan_simple"
|
||||||
|
assert create_body["handle"] == "abo-1-1"
|
||||||
|
assert create_body["customer"] == "colibre-1"
|
||||||
|
assert create_body["signup_method"] == "link"
|
||||||
|
assert "generate_handle" not in create_body
|
||||||
|
assert "prepare_subscription" not in create_body
|
||||||
|
# accept_url/cancel_url n'existent pas dans CreateSubscription : ils
|
||||||
|
# doivent passer par la session de checkout dédiée, pas ce body.
|
||||||
|
assert "accept_url" not in create_body
|
||||||
|
assert "cancel_url" not in create_body
|
||||||
|
|
||||||
|
session_call = fake_httpx["calls"][1]
|
||||||
|
assert (
|
||||||
|
session_call["url"]
|
||||||
|
== "https://checkout-api.frisbii.com/v1/session/subscription"
|
||||||
)
|
)
|
||||||
body = fake_httpx["calls"][0]["json"]
|
assert session_call["json"] == {
|
||||||
assert body["plan"] == "plan_simple"
|
"subscription": "abo-1-1",
|
||||||
assert body["handle"] == "abo-1-1"
|
"accept_url": "https://app/ok",
|
||||||
assert body["customer"] == "colibre-1"
|
"cancel_url": "https://app/ko",
|
||||||
assert body["signup_method"] == "link"
|
}
|
||||||
assert "generate_handle" not in body
|
|
||||||
assert "prepare_subscription" not in body
|
|
||||||
# accept_url/cancel_url ne sont pas des champs de CreateSubscription :
|
|
||||||
# Frisbii les ignore silencieusement s'ils sont dans le body.
|
|
||||||
assert "accept_url" not in body
|
|
||||||
assert "cancel_url" not in body
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_subscription_session_no_trial(fake_httpx):
|
def test_create_subscription_session_no_trial(fake_httpx):
|
||||||
|
fake_httpx["queue"].append(fake_httpx["Response"](200, {"handle": "abo-1-2"}))
|
||||||
fake_httpx["queue"].append(
|
fake_httpx["queue"].append(
|
||||||
fake_httpx["Response"](
|
fake_httpx["Response"](
|
||||||
200,
|
200, {"url": "https://checkout.reepay.com/#/subscription/cs_2"}
|
||||||
{
|
|
||||||
"hosted_page_links": {
|
|
||||||
"payment_info": "https://checkout.reepay.com/#/sub-2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
client.create_subscription_session(
|
client.create_subscription_session(
|
||||||
|
|||||||
Reference in New Issue
Block a user