pagopar 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pagopar-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Alan Bogarin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pagopar-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: pagopar
3
+ Version: 1.0.0
4
+ Summary: Asynchronous API wrapper for Pagopar written in Python.
5
+ Author-email: AlanBogarin <bogarin01alan@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/AlanBogarin/pagopar
8
+ Project-URL: Issues, https://github.com/AlanBogarin/pagopar/issues
9
+ Keywords: pagopar,api,async
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: aiohttp
22
+ Requires-Dist: msgspec
23
+ Dynamic: license-file
24
+
25
+ # Pagopar
26
+
27
+ Client to consume pagopar.com REST API, created based on [official documentation](https://soporte.pagopar.com/portal/es/kb).
28
+
29
+ Asynchronous Python wrapper for the Pagopar API, designed for efficiency and type safety.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install pagopar
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ You can configure the application by passing your credentials directly or by using environment variables.
40
+
41
+ ### Environment Variables
42
+
43
+ Set the following environment variables:
44
+
45
+ - `PAGOPAR_PRIVATE_TOKEN`
46
+ - `PAGOPAR_PUBLIC_TOKEN`
47
+
48
+ ### Manual Initialization
49
+
50
+ ```python
51
+ from pagopar import initialize_app
52
+
53
+ app = initialize_app(
54
+ private_token="your_private_token",
55
+ public_token="your_public_token"
56
+ )
57
+ ```
58
+
59
+ ## Usage Examples
60
+
61
+ ### Initialize Application
62
+
63
+ ```python
64
+ import asyncio
65
+ from pagopar import initialize_app, close_app
66
+
67
+ # Initialize with environment variables or pass tokens here
68
+ app = initialize_app()
69
+ other = initialize_app(
70
+ private_token="other_private_token",
71
+ public_token="other_public_token",
72
+ # When needs multiple instances, set the name as a unique identifier
73
+ name="other-app",
74
+ )
75
+
76
+ async def main():
77
+ # Your logic here
78
+ await close_app()
79
+ await close_app(other)
80
+
81
+ if __name__ == "__main__":
82
+ asyncio.run(main())
83
+ ```
84
+
85
+ ### Create a Transaction
86
+
87
+ Generate a payment link for a user.
88
+
89
+ ```python
90
+ import datetime
91
+ from pagopar import checkout, enums
92
+
93
+ async def create_order():
94
+ # Define items
95
+ item = checkout.Item(
96
+ name="Product Name",
97
+ description="Product Description",
98
+ price=150000,
99
+ quantity=1,
100
+ total_price=150000,
101
+ product_id=123,
102
+ image_url="https://example.com/image.png"
103
+ )
104
+
105
+ # Start transaction
106
+ transaction = await checkout.start_transaction(
107
+ commerce_order_id="ORDER-001",
108
+ items=[item],
109
+ amount=150000,
110
+ payment_type=enums.PaymentType.PAGO_EXPRESS,
111
+ max_payment_date=datetime.datetime.now() + datetime.timedelta(days=1),
112
+ buyer_name="John Doe",
113
+ buyer_email="john.doe@example.com",
114
+ buyer_phone="0981123456",
115
+ buyer_document="1234567",
116
+ buyer_document_type=enums.DocumentType.CI
117
+ )
118
+
119
+ # Get checkout URL
120
+ url = checkout.pagopar_checkout_url(transaction.order_id)
121
+ print(f"Checkout URL: {url}")
122
+ ```
123
+
124
+ ### Check Payment Status
125
+
126
+ Validate if a payment notification is authentic.
127
+
128
+ ```python
129
+ from pagopar import checkout
130
+
131
+ def validate_pagopar_notification(notification_token: str, order_id: str) -> bool:
132
+ return checkout.check_pagopar_payment(notification_token, order_id)
133
+ ```
134
+
135
+ ### Get Order Details
136
+
137
+ Retrieve the current status of an order.
138
+
139
+ ```python
140
+ async def check_order_status(order_hash: str):
141
+ order = await checkout.get_order(order_hash)
142
+ print(f"Order Status: {'Paid' if order.paid else 'Pending'}")
143
+ ```
144
+
145
+ ## Modules
146
+
147
+ - **checkout**: Transaction initialization, payment link generation, and order status checking.
148
+ - **courier**: Integration with shipping providers (AEX, Mobi) and freight calculation.
149
+ - **login**: Pagopar Login / Registration linking flow.
150
+ - **payment**: Recurring payments and tokenized card management.
151
+ - **subs**: Subscription notifications parsing.
152
+ - **sync**: Product and inventory synchronization with Pagopar.
@@ -0,0 +1,128 @@
1
+ # Pagopar
2
+
3
+ Client to consume pagopar.com REST API, created based on [official documentation](https://soporte.pagopar.com/portal/es/kb).
4
+
5
+ Asynchronous Python wrapper for the Pagopar API, designed for efficiency and type safety.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install pagopar
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ You can configure the application by passing your credentials directly or by using environment variables.
16
+
17
+ ### Environment Variables
18
+
19
+ Set the following environment variables:
20
+
21
+ - `PAGOPAR_PRIVATE_TOKEN`
22
+ - `PAGOPAR_PUBLIC_TOKEN`
23
+
24
+ ### Manual Initialization
25
+
26
+ ```python
27
+ from pagopar import initialize_app
28
+
29
+ app = initialize_app(
30
+ private_token="your_private_token",
31
+ public_token="your_public_token"
32
+ )
33
+ ```
34
+
35
+ ## Usage Examples
36
+
37
+ ### Initialize Application
38
+
39
+ ```python
40
+ import asyncio
41
+ from pagopar import initialize_app, close_app
42
+
43
+ # Initialize with environment variables or pass tokens here
44
+ app = initialize_app()
45
+ other = initialize_app(
46
+ private_token="other_private_token",
47
+ public_token="other_public_token",
48
+ # When needs multiple instances, set the name as a unique identifier
49
+ name="other-app",
50
+ )
51
+
52
+ async def main():
53
+ # Your logic here
54
+ await close_app()
55
+ await close_app(other)
56
+
57
+ if __name__ == "__main__":
58
+ asyncio.run(main())
59
+ ```
60
+
61
+ ### Create a Transaction
62
+
63
+ Generate a payment link for a user.
64
+
65
+ ```python
66
+ import datetime
67
+ from pagopar import checkout, enums
68
+
69
+ async def create_order():
70
+ # Define items
71
+ item = checkout.Item(
72
+ name="Product Name",
73
+ description="Product Description",
74
+ price=150000,
75
+ quantity=1,
76
+ total_price=150000,
77
+ product_id=123,
78
+ image_url="https://example.com/image.png"
79
+ )
80
+
81
+ # Start transaction
82
+ transaction = await checkout.start_transaction(
83
+ commerce_order_id="ORDER-001",
84
+ items=[item],
85
+ amount=150000,
86
+ payment_type=enums.PaymentType.PAGO_EXPRESS,
87
+ max_payment_date=datetime.datetime.now() + datetime.timedelta(days=1),
88
+ buyer_name="John Doe",
89
+ buyer_email="john.doe@example.com",
90
+ buyer_phone="0981123456",
91
+ buyer_document="1234567",
92
+ buyer_document_type=enums.DocumentType.CI
93
+ )
94
+
95
+ # Get checkout URL
96
+ url = checkout.pagopar_checkout_url(transaction.order_id)
97
+ print(f"Checkout URL: {url}")
98
+ ```
99
+
100
+ ### Check Payment Status
101
+
102
+ Validate if a payment notification is authentic.
103
+
104
+ ```python
105
+ from pagopar import checkout
106
+
107
+ def validate_pagopar_notification(notification_token: str, order_id: str) -> bool:
108
+ return checkout.check_pagopar_payment(notification_token, order_id)
109
+ ```
110
+
111
+ ### Get Order Details
112
+
113
+ Retrieve the current status of an order.
114
+
115
+ ```python
116
+ async def check_order_status(order_hash: str):
117
+ order = await checkout.get_order(order_hash)
118
+ print(f"Order Status: {'Paid' if order.paid else 'Pending'}")
119
+ ```
120
+
121
+ ## Modules
122
+
123
+ - **checkout**: Transaction initialization, payment link generation, and order status checking.
124
+ - **courier**: Integration with shipping providers (AEX, Mobi) and freight calculation.
125
+ - **login**: Pagopar Login / Registration linking flow.
126
+ - **payment**: Recurring payments and tokenized card management.
127
+ - **subs**: Subscription notifications parsing.
128
+ - **sync**: Product and inventory synchronization with Pagopar.
@@ -0,0 +1,15 @@
1
+ """
2
+ Pagopar API Wrapper
3
+ ===================
4
+
5
+ Asynchronous Python wrapper for the Pagopar API.
6
+
7
+ This library provides a simple and efficient way to interact with Pagopar services,
8
+ including payment processing, order management, and recurring payments.
9
+ """
10
+ from pagopar.app import *
11
+
12
+ from pagopar import errors as errors
13
+ from pagopar import http as http
14
+
15
+ __version__ = "1.0.0"
@@ -0,0 +1,199 @@
1
+ import os
2
+ import threading
3
+
4
+ import aiohttp
5
+
6
+ from pagopar import http as _http
7
+
8
+ __all__ = ("Application", "initialize_app", "get_app", "close_app")
9
+
10
+ _DEFAULT_APP_NAME = "<DEFAULT>"
11
+ _APP_LOCK = threading.Lock()
12
+
13
+ _apps: dict[str, "Application"] = {}
14
+
15
+
16
+ class Application:
17
+ """
18
+ Represents a Pagopar application configuration.
19
+
20
+ This class holds credential information and manages the underlying
21
+ HTTP session used for API requests.
22
+
23
+ Parameters
24
+ ----------
25
+ name : str
26
+ Unique identifier for this application instance.
27
+ private_token : str
28
+ Commerce private token provided by Pagopar.
29
+ public_token : str
30
+ Commerce public token provided by Pagopar.
31
+ proxy : str, optional
32
+ Proxy URL to use for network requests.
33
+ """
34
+
35
+ __slots__ = (
36
+ "_name",
37
+ "_private_token",
38
+ "_public_token",
39
+ "_session",
40
+ "_session_lock",
41
+ "proxy",
42
+ )
43
+
44
+ def __init__(
45
+ self,
46
+ name: str,
47
+ private_token: str,
48
+ public_token: str,
49
+ proxy: str | None
50
+ ) -> None:
51
+ self._name = name
52
+ self._private_token = private_token
53
+ self._public_token = public_token
54
+ self._session: aiohttp.ClientSession | None = None
55
+ self._session_lock = threading.Lock()
56
+ self.proxy = proxy
57
+
58
+ @property
59
+ def name(self) -> str:
60
+ """Application name."""
61
+ return self._name
62
+
63
+ @property
64
+ def private_token(self) -> str:
65
+ """Commerce private token."""
66
+ return self._private_token
67
+
68
+ @property
69
+ def public_token(self) -> str:
70
+ """Commerce public token."""
71
+ return self._public_token
72
+
73
+ @property
74
+ def session(self) -> aiohttp.ClientSession:
75
+ """
76
+ The aiohttp ClientSession used by this application.
77
+
78
+ The session is lazily created when first accessed.
79
+ """
80
+ with self._session_lock:
81
+ if not self._session or self._session.closed:
82
+ self._session = _http.create_session(self.proxy)
83
+ return self._session
84
+
85
+ def initialize_app(
86
+ private_token: str | None = None,
87
+ public_token: str | None = None,
88
+ *,
89
+ proxy: str | None = None,
90
+ name: str = _DEFAULT_APP_NAME,
91
+ ) -> Application:
92
+ """
93
+ Initialize the Pagopar application with credentials.
94
+
95
+ If tokens are not provided, the function attempts to read them from
96
+ environment variables ``PAGOPAR_PRIVATE_TOKEN`` and ``PAGOPAR_PUBLIC_TOKEN``.
97
+
98
+ Parameters
99
+ ----------
100
+ private_token : str, optional
101
+ Commerce private token.
102
+ public_token : str, optional
103
+ Commerce public token.
104
+ proxy : str, optional
105
+ Proxy URL for API requests.
106
+ name : str, optional
107
+ Unique name for this application instance. Defaults to a global default name.
108
+
109
+ Returns
110
+ -------
111
+ Application
112
+ The initialized application instance.
113
+
114
+ Raises
115
+ ------
116
+ RuntimeError
117
+ If credentials are missing (neither provided nor found in environment).
118
+ ValueError
119
+ If an application with the same name is already initialized.
120
+ """
121
+ if private_token is None or public_token is None:
122
+ private_token = os.getenv("PAGOPAR_PRIVATE_TOKEN", private_token)
123
+ public_token = os.getenv("PAGOPAR_PUBLIC_TOKEN", public_token)
124
+
125
+ if not (private_token and public_token):
126
+ raise RuntimeError("Missing pagopar commerce credentials.")
127
+
128
+ with _APP_LOCK:
129
+ if name not in _apps:
130
+ app = _apps[name] = Application(
131
+ name,
132
+ private_token,
133
+ public_token,
134
+ proxy,
135
+ )
136
+ return app
137
+
138
+ if name == _DEFAULT_APP_NAME:
139
+ raise ValueError(
140
+ "The default Pagopar app already initialized. If you want to initialize "
141
+ "multiple applications, give a unique value to the `name` parameter."
142
+ )
143
+ raise ValueError(f"Pagopar app named {name!r} already initialized.")
144
+
145
+
146
+ def get_app(name: str = _DEFAULT_APP_NAME) -> Application:
147
+ """
148
+ Retrieve an initialized application instance by name.
149
+
150
+ Parameters
151
+ ----------
152
+ name : str, optional
153
+ The name of the application to retrieve.
154
+
155
+ Returns
156
+ -------
157
+ Application
158
+ The requested application instance.
159
+
160
+ Raises
161
+ ------
162
+ ValueError
163
+ If no application with the specified name exists.
164
+ """
165
+ with _APP_LOCK:
166
+ if name in _apps:
167
+ return _apps[name]
168
+ raise ValueError(f"Pagopar named {name!r} not exists.")
169
+
170
+
171
+ async def close_app(app: Application | str | None = None) -> None:
172
+ """
173
+ Close an initialized application and its associated HTTP session.
174
+
175
+ Parameters
176
+ ----------
177
+ app : Application, str, optional
178
+ The object or name of the application to close.
179
+
180
+ Raises
181
+ ------
182
+ ValueError
183
+ If no application with the specified name exists.
184
+ """
185
+ app = check_initialized_app(app)
186
+ with _APP_LOCK:
187
+ del _apps[app.name]
188
+ if app._session and not app._session.closed:
189
+ await app.session.close()
190
+
191
+
192
+ def check_initialized_app(app: Application | str | None) -> Application:
193
+ if app is None:
194
+ return get_app()
195
+ if isinstance(app, str):
196
+ app = get_app(app)
197
+ elif app is not get_app(app.name):
198
+ raise ValueError("Application instance not initialized via the pagopar module.")
199
+ return app