diff --git a/openapi.json b/openapi.json index de333ee..04f9652 100755 --- a/openapi.json +++ b/openapi.json @@ -3030,6 +3030,171 @@ ] } }, + "/v0/merchants/{merchant_code}/readers/{reader_id}/go-checkout": { + "post": { + "operationId": "CreateGoReaderCheckout", + "summary": "Create a Go Reader Payment", + "description": "Initiates a payment on the SumUp Go terminal identified by the reader ID.\n\nUse `client_transaction_id` as an idempotency key: retrying the request with the same value returns the result of the original payment instead of creating a duplicate.", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "Access token in the format 'Bearer {token}'.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "merchant_code", + "in": "path", + "description": "Short unique identifier for the merchant.", + "required": true, + "schema": { + "type": "string", + "example": "MK10CL2A" + } + }, + { + "name": "reader_id", + "in": "path", + "description": "The unique identifier of the reader.", + "required": true, + "schema": { + "$ref": "#/components/schemas/ReaderID" + } + } + ], + "requestBody": { + "description": "Payment details to initiate on the reader.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReaderPaymentRequestParams" + } + } + } + }, + "responses": { + "200": { + "description": "Returns the result of the payment initiated on the reader.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReaderPaymentResponse" + } + } + } + }, + "400": { + "description": "The request is invalid.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Request validation failed." + } + } + } + }, + "401": { + "description": "Authentication failed or missing required scope.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/unauthorized", + "title": "Unauthorized", + "status": 401, + "detail": "Authentication credentials are missing or invalid." + } + } + } + }, + "404": { + "description": "The requested Reader resource does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/not-found", + "title": "Requested resource couldn't be found.", + "status": 404, + "detail": "The requested resource doesn't exist or does not belong to you." + } + } + } + }, + "422": { + "description": "The request could not be processed as it violates a business rule.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/validation-error", + "title": "Unprocessable Entity", + "status": 422, + "detail": "Validation failed." + } + } + } + }, + "500": { + "description": "An unexpected error occurred while processing the request.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/internal-server-error", + "title": "Internal Server Error", + "status": 500, + "detail": "An unexpected error occurred while processing the request." + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "oauth2": [ + "payments", + "readers.write" + ] + } + ], + "tags": [ + "Readers" + ], + "x-codegen": { + "method_name": "create_go_checkout", + "ignore": true + }, + "x-permissions": [ + "readers_checkout_create" + ], + "x-scopes": [ + "payments", + "readers.write" + ] + } + }, "/v0.1/memberships": { "get": { "operationId": "ListMemberships", @@ -5711,6 +5876,122 @@ "readers.write" ] } + }, + "/v0.1/merchants/{merchant_code}/readers/{reader_id}/checkout/{checkout_id}": { + "get": { + "operationId": "GetReaderCheckout", + "summary": "Get a Reader Checkout", + "description": "Get a Checkout for a Reader.\n", + "parameters": [ + { + "name": "merchant_code", + "in": "path", + "description": "Merchant Code", + "required": true, + "schema": { + "type": "string" + }, + "example": "MC0X0ABC" + }, + { + "name": "reader_id", + "in": "path", + "description": "The unique identifier of the Reader", + "required": true, + "schema": { + "type": "string" + }, + "example": "rdr_3MSAFM23CK82VSTT4BN6RWSQ65" + }, + { + "name": "checkout_id", + "in": "path", + "description": "The unique identifier of the Checkout", + "required": true, + "schema": { + "type": "string" + }, + "example": "74ecff66-1655-43ed-8ce3-193f49fa602f" + } + ], + "responses": { + "200": { + "description": "The Checkout got successfully retrieved for the given reader.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetReaderCheckoutResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateReaderCheckoutError" + } + }, + "application/problem+json": { + "example": { + "detail": "Unauthorized", + "status": 401, + "title": "Unauthorized", + "type": "https://developer.sumup.com/problem/unauthorized" + }, + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "Response when given reader or checkout is not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFound" + } + }, + "application/problem+json": { + "example": { + "detail": "The requested resource doesn't exist or does not belong to you.", + "status": 404, + "title": "Requested resource couldn't be found.", + "type": "https://developer.sumup.com/problem/not-found" + }, + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "callbacks": {}, + "security": [ + { + "apiKey": [] + }, + { + "oauth2": [ + "readers.read" + ] + } + ], + "tags": [ + "Readers" + ], + "x-codegen": { + "method_name": "get_checkout" + }, + "x-permissions": [ + "readers_checkout_view" + ], + "x-scopes": [ + "readers.read" + ] + } } }, "components": { @@ -8066,6 +8347,97 @@ "type": "string", "title": "Transaction ID" }, + "Affiliate": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "example": "com.example.app" + }, + "key": { + "type": "string", + "example": "123e4567-e89b-12d3-a456-426614174000" + } + }, + "required": [ + "app_id", + "key" + ] + }, + "Amount": { + "type": "object", + "properties": { + "currency": { + "description": "Currency ISO 4217 code", + "type": "string", + "example": "MXN" + }, + "value": { + "description": "Amount in minor units (e.g. cents).", + "type": "integer", + "example": 1000 + } + }, + "required": [ + "currency", + "value" + ] + }, + "ReaderID": { + "description": "Unique identifier of the reader that the payment is initiated on.", + "type": "string", + "example": "rdr_3MSAFM23CK82VSTT4BN6RWSQ65", + "maxLength": 30, + "minLength": 30 + }, + "ReaderPaymentRequestParams": { + "type": "object", + "properties": { + "affiliate": { + "$ref": "#/components/schemas/Affiliate" + }, + "client_transaction_id": { + "description": "Caller-supplied correlation identifier, used as the idempotency key.", + "type": "string", + "example": "19e12390-72cf-4f9f-80b5-b0c8a67fa43f" + }, + "tip_amount": { + "description": "Optional tip amount in minor units, added on top of total_amount.", + "type": "integer", + "example": 100 + }, + "total_amount": { + "$ref": "#/components/schemas/Amount" + } + }, + "required": [ + "total_amount", + "client_transaction_id" + ] + }, + "ReaderPaymentResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ReaderPaymentResponseData" + } + } + }, + "ReaderPaymentResponseData": { + "type": "object", + "properties": { + "client_transaction_id": { + "description": "Caller-supplied correlation identifier that was provided in the request.", + "type": "string", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + }, + "transaction_code": { + "description": "Transaction code returned by the acquirer/processing entity after processing the transaction.", + "type": "string", + "example": "TEENSK4W2K" + } + } + }, "MembershipStatus": { "description": "The status of the membership.", "type": "string", @@ -9246,13 +9618,6 @@ ], "title": "Reader" }, - "ReaderID": { - "description": "Unique identifier of the object.\n\nNote that this identifies the instance of the physical devices pairing with your SumUp account. If you [delete](https://developer.sumup.com/api/readers/delete-reader) a reader, and pair the device again, the ID will be different. Do not use this ID to refer to a physical device.", - "type": "string", - "example": "rdr_3MSAFM23CK82VSTT4BN6RWSQ65", - "maxLength": 30, - "minLength": 30 - }, "ReaderName": { "description": "Custom human-readable, user-defined name for easier identification of the reader.", "type": "string", @@ -9393,6 +9758,165 @@ ], "title": "CreateReaderCheckoutError" }, + "GetReaderCheckoutResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "card_type": { + "description": "Type of the card. Required for some countries", + "type": "string", + "enum": [ + "credit", + "debit" + ], + "nullable": true + }, + "checkout_id": { + "description": "Unique identifier for the checkout", + "type": "string", + "format": "uuid" + }, + "client_transaction_id": { + "description": "Client transaction identifier associated with the checkout", + "type": "string" + }, + "created_at": { + "description": "Checkout creation timestamp", + "type": "string", + "format": "date-time" + }, + "installments": { + "description": "Number of installments for the transaction. Required for some countries.", + "type": "integer", + "nullable": true + }, + "payment_failure_reason": { + "description": "Payment failure reason", + "type": "string", + "nullable": true + }, + "payment_status": { + "description": "Payment status from payments v2 event", + "type": "string", + "nullable": true + }, + "payment_type": { + "description": "Type of the payment. Required for some countries", + "type": "string", + "enum": [ + "card", + "pix" + ] + }, + "reader_firmware_version": { + "description": "Reader firmware version", + "type": "string" + }, + "reader_serial_number": { + "description": "Device serial number", + "type": "string" + }, + "status": { + "description": "Current status of the checkout", + "type": "string", + "enum": [ + "pending", + "successful", + "failed", + "cancelled" + ] + }, + "total_amount": { + "description": "Amount structure.\n\nThe amount is represented as an integer value altogether with the currency and the minor unit.\n\nFor example, EUR 1.00 is represented as value 100 with minor unit of 2.\n", + "type": "object", + "example": { + "currency": "EUR", + "minor_unit": 2, + "value": 1000 + }, + "properties": { + "currency": { + "description": "Currency ISO 4217 code", + "type": "string", + "example": "EUR" + }, + "minor_unit": { + "description": "The minor units of the currency.\nIt represents the number of decimals of the currency. For the currencies CLP, COP and HUF, the minor unit is 0.\n", + "type": "integer", + "example": 2, + "minimum": 0 + }, + "value": { + "description": "Integer value of the amount.", + "type": "integer", + "example": 1000, + "minimum": 0 + } + }, + "required": [ + "currency", + "minor_unit", + "value" + ], + "title": "Money" + }, + "updated_at": { + "description": "Checkout last update timestamp", + "type": "string", + "format": "date-time" + }, + "valid_until": { + "description": "Checkout expiration timestamp. After this time, the checkout will be automatically cancelled.", + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "required": [ + "checkout_id", + "client_transaction_id", + "reader_serial_number", + "payment_type", + "card_type", + "reader_firmware_version", + "installments", + "payment_status", + "valid_until", + "created_at", + "updated_at", + "status", + "total_amount" + ] + } + }, + "example": { + "data": { + "card_type": "credit", + "checkout_id": "00e33a36-c99b-4cb2-b635-b90c1455c9c8", + "client_transaction_id": "00e33a36-c99b-4cb2-b635-b90c1455c9c8", + "created_at": "2026-07-07T20:41:16.315434Z", + "installments": 1, + "payment_status": "pending", + "payment_type": "card", + "reader_firmware_version": "3.3.3.21", + "reader_serial_number": "1234567890", + "status": "pending", + "total_amount": { + "currency": "EUR", + "minor_unit": 2, + "value": 10000 + }, + "updated_at": "2026-07-07T20:42:18.117244Z", + "valid_until": "2026-07-07T20:41:16.315434Z" + } + }, + "required": [ + "data" + ], + "title": "GetReaderCheckoutResponse" + }, "StatusResponse": { "description": "Status of a device", "type": "object", @@ -9577,6 +10101,11 @@ "data": { "type": "object", "properties": { + "checkout_id": { + "description": "The checkout ID is a unique identifier for the checkout.\n", + "type": "string", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + }, "client_transaction_id": { "description": "The client transaction ID is a unique identifier for the transaction that is generated for the client.\n\nIt can be used later to fetch the transaction details via the [Transactions API](https://developer.sumup.com/api/transactions/get).\n", "type": "string", @@ -9590,6 +10119,7 @@ }, "example": { "data": { + "checkout_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "client_transaction_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6" } }, @@ -10676,6 +11206,70 @@ } } }, + "BadRequest": { + "description": "The request is invalid.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Request validation failed." + } + } + } + }, + "Unauthorized": { + "description": "Authentication failed or missing required scope.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/unauthorized", + "title": "Unauthorized", + "status": 401, + "detail": "Authentication credentials are missing or invalid." + } + } + } + }, + "NotFound": { + "description": "The requested Reader resource does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/not-found", + "title": "Requested resource couldn't be found.", + "status": 404, + "detail": "The requested resource doesn't exist or does not belong to you." + } + } + } + }, + "InternalError": { + "description": "An unexpected error occurred while processing the request.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://developer.sumup.com/problem/internal-server-error", + "title": "Internal Server Error", + "status": 500, + "detail": "An unexpected error occurred while processing the request." + } + } + } + }, "ListMemberships": { "description": "Returns a list of Membership objects.", "content": { @@ -10848,6 +11442,9 @@ } ] }, + { + "name": "Readers" + }, { "name": "Members", "description": "Endpoints to manage account members. Members are users that have membership within merchant accounts.", @@ -10886,15 +11483,6 @@ "$ref": "#/components/schemas/Merchant" } ] - }, - { - "name": "Readers", - "description": "A reader represents a device that accepts payments. You can use the SumUp Solo to accept in-person payments.", - "x-core-objects": [ - { - "$ref": "#/components/schemas/Reader" - } - ] } ] -} +} \ No newline at end of file diff --git a/sumup/readers/__init__.py b/sumup/readers/__init__.py index cdf0234..4d04035 100755 --- a/sumup/readers/__init__.py +++ b/sumup/readers/__init__.py @@ -5,6 +5,8 @@ ListReaders200Response, ) from ..types import ( + Affiliate, + Amount, BadRequest, CreateReaderCheckoutError, CreateReaderCheckoutRequest, @@ -12,6 +14,7 @@ CreateReaderCheckoutUnprocessableEntity, CreateReaderTerminateError, CreateReaderTerminateUnprocessableEntity, + GetReaderCheckoutResponse, Metadata, NotFound, Problem, @@ -20,6 +23,9 @@ ReaderId, ReaderName, ReaderPairingCode, + ReaderPaymentRequestParams, + ReaderPaymentResponse, + ReaderPaymentResponseData, ReaderStatus, StatusResponse, Unauthorized, @@ -30,6 +36,8 @@ "ReadersResource", "AsyncReadersResource", "ListReaders200Response", + "Affiliate", + "Amount", "BadRequest", "CreateReaderCheckoutError", "CreateReaderCheckoutRequest", @@ -37,6 +45,7 @@ "CreateReaderCheckoutUnprocessableEntity", "CreateReaderTerminateError", "CreateReaderTerminateUnprocessableEntity", + "GetReaderCheckoutResponse", "Metadata", "NotFound", "Problem", @@ -45,6 +54,9 @@ "ReaderId", "ReaderName", "ReaderPairingCode", + "ReaderPaymentRequestParams", + "ReaderPaymentResponse", + "ReaderPaymentResponseData", "ReaderStatus", "StatusResponse", "Unauthorized", diff --git a/sumup/readers/resource.py b/sumup/readers/resource.py index f681e53..7f601d0 100755 --- a/sumup/readers/resource.py +++ b/sumup/readers/resource.py @@ -1,9 +1,5 @@ # Code generated by `py-sdk-gen`. DO NOT EDIT. # ruff: noqa: F401, F541 -""" -A reader represents a device that accepts payments. You can use the SumUp Solo to accept in-person payments. -""" - from __future__ import annotations from .._service import ( Resource, @@ -16,6 +12,8 @@ ) from .._exceptions import APIError from ..types import ( + Affiliate, + Amount, BadRequest, CreateReaderCheckoutError, CreateReaderCheckoutRequest, @@ -23,6 +21,7 @@ CreateReaderCheckoutUnprocessableEntity, CreateReaderTerminateError, CreateReaderTerminateUnprocessableEntity, + GetReaderCheckoutResponse, Metadata, NotFound, Problem, @@ -31,16 +30,22 @@ ReaderId, ReaderName, ReaderPairingCode, + ReaderPaymentRequestParams, + ReaderPaymentResponse, + ReaderPaymentResponseData, ReaderStatus, StatusResponse, Unauthorized, ) from ..types import ( + AffiliateInput, + AmountInput, CreateReaderCheckoutRequestInput, MetadataInput, ReaderIdInput, ReaderNameInput, ReaderPairingCodeInput, + ReaderPaymentRequestParamsInput, ) import datetime import httpx @@ -49,6 +54,31 @@ import typing_extensions +class CreateGoReaderCheckoutBodyInput(typing_extensions.TypedDict, total=False): + """ + CreateGoReaderCheckoutBody is a schema definition. + """ + + client_transaction_id: typing_extensions.Required[ + typing_extensions.Annotated[ + str, + typing_extensions.Doc( + "Caller-supplied correlation identifier, used as the idempotency key." + ), + ] + ] + total_amount: typing_extensions.Required[AmountInput] + affiliate: typing_extensions.NotRequired[AffiliateInput] + tip_amount: typing_extensions.NotRequired[ + typing_extensions.Annotated[ + int, + typing_extensions.Doc( + "Optional tip amount in minor units, added on top of total_amount." + ), + ] + ] + + class CreateReaderBodyInput(typing_extensions.TypedDict, total=False): """ CreateReaderBody is a schema definition. @@ -294,6 +324,78 @@ class ReadersResource(Resource): def __init__(self, client: httpx.Client) -> None: super().__init__(client) + def create_go_checkout( + self, + merchant_code: str, + reader_id: ReaderId, + *, + affiliate: typing.Union[AffiliateInput, None, NotGivenType] = NOT_GIVEN, + client_transaction_id: str, + tip_amount: typing.Union[int, None, NotGivenType] = NOT_GIVEN, + total_amount: AmountInput, + headers: typing.Optional[HeaderTypes] = None, + ) -> ReaderPaymentResponse: + """ + Create a Go Reader Payment + + Initiates a payment on the SumUp Go terminal identified by the reader ID. + + Use `client_transaction_id` as an idempotency key: retrying the request with the same value returns the result ofthe original payment instead of creating a duplicate. + + + Raises: + APIError: Raised when the API returns one of the documented error responses: + 400: The request is invalid. + 401: Authentication failed or missing required scope. + 404: The requested Reader resource does not exist. + 422: The request could not be processed as it violates a business rule. + 500: An unexpected error occurred while processing the request. + Unexpected response statuses also raise this exception. + """ + body_data: dict[str, typing.Any] = {} + if not isinstance(affiliate, NotGivenType): + body_data["affiliate"] = affiliate + body_data["client_transaction_id"] = client_transaction_id + if not isinstance(tip_amount, NotGivenType): + body_data["tip_amount"] = tip_amount + body_data["total_amount"] = total_amount + + resp = self._client.post( + f"/v0/merchants/{merchant_code}/readers/{reader_id}/go-checkout", + json=serialize_request_data(body_data), + headers=headers, + ) + if resp.status_code == 200: + return pydantic.TypeAdapter(ReaderPaymentResponse).validate_python(resp.json()) + elif resp.status_code == 400: + raise APIError("The request is invalid.", status=resp.status_code, body=resp.text) + elif resp.status_code == 401: + raise APIError( + "Authentication failed or missing required scope.", + status=resp.status_code, + body=resp.text, + ) + elif resp.status_code == 404: + raise APIError( + "The requested Reader resource does not exist.", + status=resp.status_code, + body=resp.text, + ) + elif resp.status_code == 422: + raise APIError( + "The request could not be processed as it violates a business rule.", + status=resp.status_code, + body=resp.text, + ) + elif resp.status_code == 500: + raise APIError( + "An unexpected error occurred while processing the request.", + status=resp.status_code, + body=resp.text, + ) + else: + raise APIError(f"Unexpected response", status=resp.status_code, body=resp.text) + def list( self, merchant_code: str, headers: typing.Optional[HeaderTypes] = None ) -> ListReaders200Response: @@ -690,6 +792,43 @@ def terminate_checkout( else: raise APIError(f"Unexpected response", status=resp.status_code, body=resp.text) + def get_checkout( + self, + merchant_code: str, + reader_id: str, + checkout_id: str, + headers: typing.Optional[HeaderTypes] = None, + ) -> GetReaderCheckoutResponse: + """ + Get a Reader Checkout + + Get a Checkout for a Reader. + + + + Raises: + APIError: Raised when the API returns one of the documented error responses: + 401: Unauthorized + 404: Response when given reader or checkout is not found + Unexpected response statuses also raise this exception. + """ + resp = self._client.get( + f"/v0.1/merchants/{merchant_code}/readers/{reader_id}/checkout/{checkout_id}", + headers=headers, + ) + if resp.status_code == 200: + return pydantic.TypeAdapter(GetReaderCheckoutResponse).validate_python(resp.json()) + elif resp.status_code == 401: + raise APIError("Unauthorized", status=resp.status_code, body=resp.text) + elif resp.status_code == 404: + raise APIError( + "Response when given reader or checkout is not found", + status=resp.status_code, + body=resp.text, + ) + else: + raise APIError(f"Unexpected response", status=resp.status_code, body=resp.text) + class AsyncReadersResource(AsyncResource): """Async API resource for the Readers endpoints.""" @@ -697,6 +836,78 @@ class AsyncReadersResource(AsyncResource): def __init__(self, client: httpx.AsyncClient) -> None: super().__init__(client) + async def create_go_checkout( + self, + merchant_code: str, + reader_id: ReaderId, + *, + affiliate: typing.Union[AffiliateInput, None, NotGivenType] = NOT_GIVEN, + client_transaction_id: str, + tip_amount: typing.Union[int, None, NotGivenType] = NOT_GIVEN, + total_amount: AmountInput, + headers: typing.Optional[HeaderTypes] = None, + ) -> ReaderPaymentResponse: + """ + Create a Go Reader Payment + + Initiates a payment on the SumUp Go terminal identified by the reader ID. + + Use `client_transaction_id` as an idempotency key: retrying the request with the same value returns the result ofthe original payment instead of creating a duplicate. + + + Raises: + APIError: Raised when the API returns one of the documented error responses: + 400: The request is invalid. + 401: Authentication failed or missing required scope. + 404: The requested Reader resource does not exist. + 422: The request could not be processed as it violates a business rule. + 500: An unexpected error occurred while processing the request. + Unexpected response statuses also raise this exception. + """ + body_data: dict[str, typing.Any] = {} + if not isinstance(affiliate, NotGivenType): + body_data["affiliate"] = affiliate + body_data["client_transaction_id"] = client_transaction_id + if not isinstance(tip_amount, NotGivenType): + body_data["tip_amount"] = tip_amount + body_data["total_amount"] = total_amount + + resp = await self._client.post( + f"/v0/merchants/{merchant_code}/readers/{reader_id}/go-checkout", + json=serialize_request_data(body_data), + headers=headers, + ) + if resp.status_code == 200: + return pydantic.TypeAdapter(ReaderPaymentResponse).validate_python(resp.json()) + elif resp.status_code == 400: + raise APIError("The request is invalid.", status=resp.status_code, body=resp.text) + elif resp.status_code == 401: + raise APIError( + "Authentication failed or missing required scope.", + status=resp.status_code, + body=resp.text, + ) + elif resp.status_code == 404: + raise APIError( + "The requested Reader resource does not exist.", + status=resp.status_code, + body=resp.text, + ) + elif resp.status_code == 422: + raise APIError( + "The request could not be processed as it violates a business rule.", + status=resp.status_code, + body=resp.text, + ) + elif resp.status_code == 500: + raise APIError( + "An unexpected error occurred while processing the request.", + status=resp.status_code, + body=resp.text, + ) + else: + raise APIError(f"Unexpected response", status=resp.status_code, body=resp.text) + async def list( self, merchant_code: str, headers: typing.Optional[HeaderTypes] = None ) -> ListReaders200Response: @@ -1092,3 +1303,40 @@ async def terminate_checkout( ) else: raise APIError(f"Unexpected response", status=resp.status_code, body=resp.text) + + async def get_checkout( + self, + merchant_code: str, + reader_id: str, + checkout_id: str, + headers: typing.Optional[HeaderTypes] = None, + ) -> GetReaderCheckoutResponse: + """ + Get a Reader Checkout + + Get a Checkout for a Reader. + + + + Raises: + APIError: Raised when the API returns one of the documented error responses: + 401: Unauthorized + 404: Response when given reader or checkout is not found + Unexpected response statuses also raise this exception. + """ + resp = await self._client.get( + f"/v0.1/merchants/{merchant_code}/readers/{reader_id}/checkout/{checkout_id}", + headers=headers, + ) + if resp.status_code == 200: + return pydantic.TypeAdapter(GetReaderCheckoutResponse).validate_python(resp.json()) + elif resp.status_code == 401: + raise APIError("Unauthorized", status=resp.status_code, body=resp.text) + elif resp.status_code == 404: + raise APIError( + "Response when given reader or checkout is not found", + status=resp.status_code, + body=resp.text, + ) + else: + raise APIError(f"Unexpected response", status=resp.status_code, body=resp.text) diff --git a/sumup/types/__init__.py b/sumup/types/__init__.py index ca05f85..406ad47 100755 --- a/sumup/types/__init__.py +++ b/sumup/types/__init__.py @@ -206,6 +206,54 @@ class AddressLegacyDict(typing_extensions.TypedDict, total=False): AddressLegacyInput = AddressLegacyDict +class Affiliate(pydantic.BaseModel): + """ + Affiliate is a schema definition. + """ + + app_id: str + + key: str + + +class AffiliateDict(typing_extensions.TypedDict, total=False): + app_id: typing_extensions.Required[str] + key: typing_extensions.Required[str] + + +AffiliateInput = AffiliateDict + + +class Amount(pydantic.BaseModel): + """ + Amount is a schema definition. + """ + + currency: str + """ + Currency ISO 4217 code + """ + + value: int + """ + Amount in minor units (e.g. cents). + """ + + +class AmountDict(typing_extensions.TypedDict, total=False): + currency: typing_extensions.Required[ + typing_extensions.Annotated[str, typing_extensions.Doc("Currency ISO 4217 code")] + ] + value: typing_extensions.Required[ + typing_extensions.Annotated[ + int, typing_extensions.Doc("Amount in minor units (e.g. cents).") + ] + ] + + +AmountInput = AmountDict + + Attributes = dict[str, object] AttributesInput = typing.Mapping[str, object] """ @@ -2040,6 +2088,11 @@ class CreateReaderCheckoutResponseData(pydantic.BaseModel): It can be used later to fetch the transaction details via the [Transactions API](https://developer.sumup.com/api/transactions/get). """ + checkout_id: typing.Optional[str] = None + """ + The checkout ID is a unique identifier for the checkout. + """ + class CreateReaderCheckoutResponse(pydantic.BaseModel): """ @@ -2526,6 +2579,132 @@ class FinancialPayout(pydantic.BaseModel): Ordered list of payout and payout-deduction records. """ +GetReaderCheckoutResponseDataCardType = typing.Union[typing.Literal["credit", "debit"], str] + +GetReaderCheckoutResponseDataPaymentType = typing.Union[typing.Literal["card", "pix"], str] + +GetReaderCheckoutResponseDataStatus = typing.Union[ + typing.Literal["cancelled", "failed", "pending", "successful"], str +] + + +class GetReaderCheckoutResponseDataTotalAmount(pydantic.BaseModel): + """ + Amount structure. + + The amount is represented as an integer value altogether with the currency and the minor unit. + + For example, EUR 1.00 is represented as value 100 with minor unit of 2. + """ + + currency: str + """ + Currency ISO 4217 code + """ + + minor_unit: int + """ + The minor units of the currency. + It represents the number of decimals of the currency. For the currencies CLP, COP and HUF, the minor unit is0. + Min: 0 + """ + + value: int + """ + Integer value of the amount. + Min: 0 + """ + + +class GetReaderCheckoutResponseData(pydantic.BaseModel): + """ + GetReaderCheckoutResponseData is a schema definition. + """ + + card_type: GetReaderCheckoutResponseDataCardType + """ + Type of the card. Required for some countries + """ + + checkout_id: str + """ + Unique identifier for the checkout + Format: uuid + """ + + client_transaction_id: str + """ + Client transaction identifier associated with the checkout + """ + + created_at: datetime.datetime + """ + Checkout creation timestamp + """ + + installments: int + """ + Number of installments for the transaction. Required for some countries. + """ + + payment_status: str + """ + Payment status from payments v2 event + """ + + payment_type: GetReaderCheckoutResponseDataPaymentType + """ + Type of the payment. Required for some countries + """ + + reader_firmware_version: str + """ + Reader firmware version + """ + + reader_serial_number: str + """ + Device serial number + """ + + status: GetReaderCheckoutResponseDataStatus + """ + Current status of the checkout + """ + + total_amount: GetReaderCheckoutResponseDataTotalAmount + """ + Amount structure. + + The amount is represented as an integer value altogether with the currency and the minor unit. + + For example, EUR 1.00 is represented as value 100 with minor unit of 2. + """ + + updated_at: datetime.datetime + """ + Checkout last update timestamp + """ + + valid_until: datetime.datetime + """ + Checkout expiration timestamp. After this time, the checkout will be automatically cancelled. + """ + + payment_failure_reason: typing.Optional[str] = None + """ + Payment failure reason + """ + + +class GetReaderCheckoutResponse(pydantic.BaseModel): + """ + GetReaderCheckoutResponse is a schema definition. + """ + + data: GetReaderCheckoutResponseData + + HorizontalAccuracy = float """ Indication of the precision of the geographical position received from the payment terminal. @@ -3547,10 +3726,8 @@ class ReaderDevice(pydantic.BaseModel): ReaderId = str ReaderIdInput = str """ -Unique identifier of the object. - -Note that this identifies the instance of the physical devices pairing with your SumUp account. If you [delete](https://developer.sumup.com/api/readers/delete-reader) areader, and pair the device again, the ID will be different. Do not use this ID to refer to a physical device. -Minlength: 30 +Unique identifier of the reader that the payment is initiated on. +Min length: 30 Max length: 30 """ @@ -3581,10 +3758,8 @@ class Reader(pydantic.BaseModel): id: ReaderId """ - Unique identifier of the object. - - Note that this identifies the instance of the physical devices pairing with your SumUp account. If you [delete](https://developer.sumup.com/api/readers/delete-reader) areader, and pair the device again, the ID will be different. Do not use this ID to refer to a physical device. - Minlength: 30 + Unique identifier of the reader that the payment is initiated on. + Min length: 30 Max length: 30 """ @@ -3694,6 +3869,74 @@ class ReaderCheckoutStatusChange(pydantic.BaseModel): """ +class ReaderPaymentRequestParams(pydantic.BaseModel): + """ + ReaderPaymentRequestParams is a schema definition. + """ + + client_transaction_id: str + """ + Caller-supplied correlation identifier, used as the idempotency key. + """ + + total_amount: Amount + + affiliate: typing.Optional[Affiliate] = None + + tip_amount: typing.Optional[int] = None + """ + Optional tip amount in minor units, added on top of total_amount. + """ + + +class ReaderPaymentRequestParamsDict(typing_extensions.TypedDict, total=False): + client_transaction_id: typing_extensions.Required[ + typing_extensions.Annotated[ + str, + typing_extensions.Doc( + "Caller-supplied correlation identifier, used as the idempotency key." + ), + ] + ] + total_amount: typing_extensions.Required[AmountInput] + affiliate: typing_extensions.NotRequired[AffiliateInput] + tip_amount: typing_extensions.NotRequired[ + typing_extensions.Annotated[ + int, + typing_extensions.Doc( + "Optional tip amount in minor units, added on top of total_amount." + ), + ] + ] + + +ReaderPaymentRequestParamsInput = ReaderPaymentRequestParamsDict + + +class ReaderPaymentResponseData(pydantic.BaseModel): + """ + ReaderPaymentResponseData is a schema definition. + """ + + client_transaction_id: typing.Optional[str] = None + """ + Caller-supplied correlation identifier that was provided in the request. + """ + + transaction_code: typing.Optional[str] = None + """ + Transaction code returned by the acquirer/processing entity after processing the transaction. + """ + + +class ReaderPaymentResponse(pydantic.BaseModel): + """ + ReaderPaymentResponse is a schema definition. + """ + + data: typing.Optional[ReaderPaymentResponseData] = None + + class ReceiptCard(pydantic.BaseModel): """ Payment card details displayed on the receipt.