diff --git a/buckaroo/builders/payments/in3_builder.py b/buckaroo/builders/payments/in3_builder.py index 71e5dab..373d6b5 100644 --- a/buckaroo/builders/payments/in3_builder.py +++ b/buckaroo/builders/payments/in3_builder.py @@ -25,6 +25,13 @@ def get_allowed_service_parameters(self, action: str = "Pay") -> Dict[str, Any]: "description": "Shipping customer information", }, "article": {"type": list, "required": True, "description": "IN3 articles"}, + "route": { + "type": str, + "required": False, + "description": ( + "In3 acquirer route, e.g. 'abn_b2b' for ABN-AMRO Achteraf Betalen" + ), + }, } return {} diff --git a/examples/abn_amro_achteraf_betalen.py b/examples/abn_amro_achteraf_betalen.py new file mode 100644 index 0000000..5f38bc0 --- /dev/null +++ b/examples/abn_amro_achteraf_betalen.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +"""Demo of ABN-AMRO Achteraf Betalen via the In3 payment method. + +Same underlying service as In3 V3 — setting the optional ``Route`` service +parameter to ``abn_b2b`` selects ABN-AMRO Achteraf Betalen instead of the +default In3 acquirer. NL/B2B only. Gated on ``BUCKAROO_STORE_KEY`` / +``BUCKAROO_SECRET_KEY`` env vars. +""" + +import os +import sys + +# Add parent directory to Python path so the demo can import the SDK in-place. +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +from buckaroo.app import Buckaroo + + +def _have_credentials() -> bool: + if not os.getenv("BUCKAROO_STORE_KEY") or not os.getenv("BUCKAROO_SECRET_KEY"): + print("⚠️ Set BUCKAROO_STORE_KEY and BUCKAROO_SECRET_KEY to run this demo") + return False + return True + + +def demo_abn_amro_achteraf_betalen() -> None: + """Pay via In3 with the ABN-AMRO Achteraf Betalen route.""" + print("\nABN-AMRO Achteraf Betalen (In3, Route=abn_b2b)") + print("-" * 40) + if not _have_credentials(): + return + + try: + app = Buckaroo.from_env() + + # create_payment(method, params) dispatches through the PaymentMethodFactory + # and returns a ready-to-execute In3Builder. + builder = app.payments.create_payment( + "in3", + { + "currency": "EUR", + "amount": 121.00, + "description": "ABN-AMRO Achteraf Betalen demo", + "invoice": "ABN-B2B-DEMO-001", + "return_url": "https://www.buckaroo.nl", + "return_url_cancel": "https://www.buckaroo.nl/cancel", + "return_url_error": "https://www.buckaroo.nl/error", + "return_url_reject": "https://www.buckaroo.nl/reject", + "service_parameters": { + "billingCustomer": [ + { + "firstName": "John", + "lastName": "Doe", + "chamberOfCommerce": "12345678", + "companyName": "Acme B.V.", + } + ], + "shippingCustomer": [ + { + "firstName": "John", + "lastName": "Doe", + "chamberOfCommerce": "12345678", + "companyName": "Acme B.V.", + } + ], + "article": [ + {"description": "Widget", "quantity": "1", "price": "100.00"}, + ], + }, + }, + ) + + # Route is optional and NL/B2B only - selects ABN-AMRO Achteraf Betalen + # instead of the default In3 acquirer. + builder.add_parameter("route", "abn_b2b") + + response = builder.pay() + + print(f" status.code={response.status.code.code} key={response.key}") + except Exception as e: + print(f" ❌ {e}") + + +def main() -> None: + print("BUCKAROO SDK — ABN-AMRO ACHTERAF BETALEN DEMO") + print("=" * 60) + print("Env vars read:") + print(" BUCKAROO_STORE_KEY, BUCKAROO_SECRET_KEY, BUCKAROO_MODE") + + demo_abn_amro_achteraf_betalen() + + print("\n" + "=" * 60) + print("done.") + + +if __name__ == "__main__": + main() diff --git a/tests/unit/builders/payments/test_in3_builder.py b/tests/unit/builders/payments/test_in3_builder.py index fef7206..ecaa087 100644 --- a/tests/unit/builders/payments/test_in3_builder.py +++ b/tests/unit/builders/payments/test_in3_builder.py @@ -43,9 +43,21 @@ def test_get_allowed_service_parameters_pay_snapshot(client): "required": True, "description": "IN3 articles", }, + "route": { + "type": str, + "required": False, + "description": ("In3 acquirer route, e.g. 'abn_b2b' for ABN-AMRO Achteraf Betalen"), + }, } +def test_get_allowed_service_parameters_pay_allows_optional_route(client): + allowed = In3Builder(client).get_allowed_service_parameters("Pay") + + assert "route" in allowed + assert allowed["route"]["required"] is False + + def test_get_allowed_service_parameters_pay_is_case_insensitive(client): builder = In3Builder(client) @@ -59,6 +71,37 @@ def test_get_allowed_service_parameters_non_pay_returns_empty(client, action): assert In3Builder(client).get_allowed_service_parameters(action) == {} +def test_build_pay_with_route_keeps_route_parameter(client): + request = ( + populate_required_fields(In3Builder(client), amount=99.95) + .add_parameter("billingCustomer", [{"Name": "John"}]) + .add_parameter("shippingCustomer", [{"Name": "John"}]) + .add_parameter("article", [{"Description": "Widget", "Quantity": 1}]) + .add_parameter("route", "abn_b2b") + .build("Pay") + ) + + service = request.services.services[0] + route_params = [p for p in service.parameters if p.name == "Route"] + + assert len(route_params) == 1 + assert route_params[0].value == "abn_b2b" + + +def test_build_pay_without_route_is_unchanged(client): + request = ( + populate_required_fields(In3Builder(client), amount=99.95) + .add_parameter("billingCustomer", [{"Name": "John"}]) + .add_parameter("shippingCustomer", [{"Name": "John"}]) + .add_parameter("article", [{"Description": "Widget", "Quantity": 1}]) + .build("Pay") + ) + + service = request.services.services[0] + + assert all(p.name != "Route" for p in service.parameters) + + def test_pay_posts_transaction_and_parses_response(client, mock_strategy): mock_strategy.queue( BuckarooMockRequest.json(