payra-sdk 1.2.4__py3-none-any.whl → 1.2.6__py3-none-any.whl

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.
@@ -25,78 +25,107 @@ class PayraOrderVerification:
25
25
  raise ConnectionError(f"Failed to connect to QuickNode RPC for {self.network}")
26
26
 
27
27
  self.merchant_id = os.getenv(f"PAYRA_{self.network}_MERCHANT_ID")
28
- self.forward_address = os.getenv(f"PAYRA_{self.network}_CORE_FORWARD_CONTRACT_ADDRESS")
28
+ self.gateway_address = os.getenv(f"PAYRA_{self.network}_OCP_GATEWAY_CONTRACT_ADDRESS")
29
29
 
30
30
  if not self.merchant_id:
31
31
  raise InvalidArgumentError(f"Missing PAYRA_{self.network}_MERCHANT_ID in .env")
32
- if not self.forward_address:
33
- raise InvalidArgumentError(f"Missing PAYRA_{self.network}_CORE_FORWARD_CONTRACT_ADDRESS in .env")
32
+ if not self.gateway_address:
33
+ raise InvalidArgumentError(f"Missing PAYRA_{self.network}_OCP_GATEWAY_CONTRACT_ADDRESS in .env")
34
34
 
35
35
  # Load ABI
36
36
  abi_path = os.path.join(os.path.dirname(__file__), "contracts", "payraABI.json")
37
37
  with open(abi_path, "r") as f:
38
38
  self.abi = json.load(f)
39
39
 
40
- # find ABI parts
41
- self.core_fn = PayraUtils.find_function(self.abi, "isOrderPaid")
42
- self.forward_fn = PayraUtils.find_function(self.abi, "forward")
40
+ self.user_data_contract = self.get_user_data_contract()
43
41
 
44
- # prepare forward contract
45
- self.forward_contract = self.web3.eth.contract(
46
- address=self.web3.to_checksum_address(self.forward_address),
47
- abi=[self.forward_fn]
48
- )
49
-
50
- def get_rpc_url(self, network: str) -> str:
42
+ def get_user_data_contract(self):
51
43
  """
52
- Collects all PAYRA_{NETWORK}_RPC_URL_i variables and randomly picks one.
44
+ Internal helper to initialize Payra contracts.
45
+ It fetches the UserData contract address from the Gateway registry.
53
46
  """
54
- urls = []
55
- i = 1
56
- while True:
57
- env_key = f"PAYRA_{network}_RPC_URL_{i}"
58
- value = os.getenv(env_key)
59
- if not value:
60
- break
61
- urls.append(value.strip())
62
- i += 1
63
-
64
- if not urls:
65
- raise InvalidArgumentError(f"No RPC URLs found for network: {network}")
66
-
67
- return random.choice(urls)
47
+ # Initialize Gateway Contract
48
+ gateway_contract = self.web3.eth.contract(address=self.gateway_address, abi=self.abi)
49
+
50
+ # getRegistryDetails
51
+ _, _, user_data_address, _ = gateway_contract.functions.getRegistryDetails().call()
52
+
53
+ # Return the actual contract responsible for order data
54
+ return self.web3.eth.contract(address=user_data_address, abi=self.abi)
68
55
 
69
56
  def is_order_paid(self, order_id: str) -> dict:
70
57
  """
71
- Calls the Payra Forward contract to check if an order is paid.
58
+ Verify if an order is paid on Payra contract.
72
59
  """
73
60
  try:
74
- # encode isOrderPaid call manually
75
- core_fn_selector = PayraUtils.function_selector(self.core_fn)
76
- encoded_params = self.web3.codec.encode(
77
- [inp["type"] for inp in self.core_fn["inputs"]],
78
- [int(self.merchant_id), order_id]
79
- )
80
- data = core_fn_selector + encoded_params.hex()
81
-
82
- # call forward()
83
- result = self.forward_contract.functions.forward("0x" + data).call()
84
-
85
- # decode result
86
- decoded = self.web3.codec.decode(
87
- [out["type"] for out in self.core_fn["outputs"]],
88
- result
89
- )
61
+ # Using the resolved user_data_contract
62
+ is_paid = self.user_data_contract.functions.isOrderPaid(
63
+ int(self.merchant_id),
64
+ order_id
65
+ ).call()
90
66
 
91
67
  return {
92
68
  "success": True,
93
- "paid": bool(decoded[0]),
69
+ "paid": bool(is_paid),
94
70
  "error": None
95
71
  }
96
-
97
72
  except Exception as e:
98
73
  return {
99
74
  "success": False,
100
75
  "paid": None,
101
76
  "error": str(e)
102
77
  }
78
+
79
+ def get_order_status(self, order_id: str) -> dict:
80
+ """
81
+ Detailed status of an order from Payra smart contract.
82
+ Equivalent to getOrderDetails in Node.js version.
83
+ """
84
+ try:
85
+ # Calls getOrderDetails
86
+ order = self.user_data_contract.functions.getOrderDetails(
87
+ int(self.merchant_id),
88
+ order_id
89
+ ).call()
90
+
91
+ # Mapping the returned tuple/struct to a dictionary
92
+ # Order struct: [paid, token, amount, fee, timestamp]
93
+ return {
94
+ "success": True,
95
+ "error": None,
96
+ "paid": bool(order[0]),
97
+ "token": order[1],
98
+ "amount": int(order[2]),
99
+ "fee": int(order[3]),
100
+ "timestamp": int(order[4]),
101
+ }
102
+ except Exception as e:
103
+ return {
104
+ "success": False,
105
+ "error": str(e),
106
+ "paid": None,
107
+ "token": None,
108
+ "amount": None,
109
+ "fee": None,
110
+ "timestamp": None,
111
+ }
112
+
113
+ def get_rpc_url(self, network: str) -> str:
114
+ """
115
+ Collects all PAYRA_{NETWORK}_RPC_URL_i variables and randomly picks one.
116
+ """
117
+
118
+ urls = []
119
+ i = 1
120
+ while True:
121
+ env_key = f"PAYRA_{network}_RPC_URL_{i}"
122
+ value = os.getenv(env_key)
123
+ if not value:
124
+ break
125
+ urls.append(value.strip())
126
+ i += 1
127
+
128
+ if not urls:
129
+ raise InvalidArgumentError(f"No RPC URLs found for network: {network}")
130
+
131
+ return random.choice(urls)
payra_sdk/signature.py CHANGED
@@ -187,20 +187,20 @@ class PayraSignatureGenerator:
187
187
  # get network env
188
188
  def get_credentials_for_network(self, network: str) -> tuple[str, int]:
189
189
  """
190
- Returns (private_key, merchant_id) for the given network using dynamic env keys like:
191
- PAYRA_POLYGON_PRIVATE_KEY, PAYRA_POLYGON_MERCHANT_ID
190
+ Returns (signature_key, merchant_id) for the given network using dynamic env keys like:
191
+ PAYRA_POLYGON_SIGNATURE_KEY, PAYRA_POLYGON_MERCHANT_ID
192
192
  """
193
193
 
194
- private_key = os.getenv(f"PAYRA_{network.upper()}_PRIVATE_KEY")
194
+ signature_key = os.getenv(f"PAYRA_{network.upper()}_SIGNATURE_KEY")
195
195
  merchant_id = os.getenv(f"PAYRA_{network.upper()}_MERCHANT_ID")
196
196
 
197
- if not private_key or not merchant_id:
198
- raise ValueError(f"Missing credentials for network '{network}' in .env (checked {private_key_key}, {merchant_id_key})")
197
+ if not signature_key or not merchant_id:
198
+ raise ValueError(f"Missing credentials for network '{network}' in .env (checked {signature_key}, {merchant_id_key})")
199
199
 
200
- return private_key, int(merchant_id)
200
+ return signature_key, int(merchant_id)
201
201
 
202
202
  def get_account_address(self, network: str) -> str:
203
- private_key, merchant_id = self.get_credentials_for_network(network)
204
- account = Account.from_key(private_key)
203
+ signature_key, merchant_id = self.get_credentials_for_network(network)
204
+ account = Account.from_key(signature_key)
205
205
 
206
206
  return account.address
payra_sdk/utils.py CHANGED
@@ -18,24 +18,6 @@ class PayraUtils:
18
18
  _cache_dir = Path.home() / ".payra_cache"
19
19
  _cache_file = _cache_dir / "payra_cash_exchange_rate_cache.json"
20
20
 
21
- @staticmethod
22
- def find_function(abi: list[Dict[str, Any]], name: str) -> Dict[str, Any]:
23
- """
24
- Finds a function definition by name in the given ABI.
25
- """
26
- for entry in abi:
27
- if entry.get("type") == "function" and entry.get("name") == name:
28
- return entry
29
- raise InvalidArgumentError(f"Function {name} not found in ABI!")
30
-
31
- @staticmethod
32
- def function_selector(fn: Dict[str, Any]) -> str:
33
- """
34
- Generates the function selector (first 4 bytes of keccak of signature).
35
- """
36
- signature = f"{fn['name']}({','.join([inp['type'] for inp in fn['inputs']])})"
37
- return Web3.keccak(text=signature)[:4].hex()
38
-
39
21
  # === Conversion Helpers ===
40
22
  @staticmethod
41
23
  def to_wei(amount: Union[int, float, str], chain: str, token: str) -> int:
@@ -0,0 +1,410 @@
1
+ Metadata-Version: 2.4
2
+ Name: payra_sdk
3
+ Version: 1.2.6
4
+ Summary: Python SDK for Payra payment signature generation (backend)
5
+ Author: Your Name
6
+ Author-email: Wraith <support@payra.cash>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2025 Wraith
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy...
12
+
13
+ Project-URL: Homepage, https://github.com/payracash
14
+ Project-URL: Source, https://github.com/payracash/payra-sdk-python
15
+ Project-URL: Issues, https://github.com/payracash/payra-sdk-python/issues
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: web3>=6.0.0
20
+ Requires-Dist: eth-abi>=4.0.0
21
+ Requires-Dist: eth-account>=0.10.0
22
+ Requires-Dist: python-dotenv>=1.0.0
23
+ Dynamic: license-file
24
+
25
+ # Payra Python SDK
26
+
27
+ Official **Python SDK** for integrating **Payra's on-chain payment system** into your backend applications.
28
+
29
+ This SDK provides:
30
+ - Secure generation of **ECDSA signatures** compatible with the Payra smart contract, used for order payment verification.
31
+ - Simple methods for **checking the on-chain status of orders** to confirm completed payments.
32
+
33
+
34
+ ## How It Works
35
+
36
+ The typical flow for signing and verifying a Payra transaction:
37
+ 1. The **frontend** prepares all required payment parameters:
38
+ - **Network** – blockchain name (e.g. Polygon, Linea)
39
+ - **Token address** – ERC-20 token contract address
40
+ - **Order ID** – unique order identifier
41
+ - **Amount WEI** – already converted to the smallest unit (e.g. wei, 10⁶)
42
+ - **Timestamp** – Unix timestamp of the order
43
+ - **Payer wallet address** – the wallet address from which the user will make the on-chain payment
44
+ 2. The frontend sends these parameters to your **backend**.
45
+ 3. The **backend** uses this SDK to generate a cryptographic **ECDSA signature** with its signature key (performed **offline**).
46
+ 4. The backend returns the generated signature to the frontend.
47
+ 5. The **frontend** calls the Payra smart contract (`payOrder`) with all parameters **plus** the signature.
48
+
49
+ This process ensures full compatibility between your backend and Payra’s on-chain verification logic.
50
+
51
+
52
+ ## Features
53
+
54
+ - Generates **Ethereum ECDSA signatures** using the `secp256k1` curve.
55
+ - Fully compatible with **Payra's Solidity smart contracts** (`ERC-1155` payment verification).
56
+ - Supports `.env` and `config/payra.php` configuration for multiple blockchain networks.
57
+ - Laravel IoC container integration (easy dependency injection)
58
+ - Verifies **order payment status directly on-chain** via RPC or blockchain explorer API.
59
+ - Provides **secure backend integration** for signing and verifying transactions.
60
+ - Includes optional utility helpers for:
61
+ - **Currency conversion** (via [ExchangeRate API](https://www.exchangerate-api.com/))
62
+ - **USD ⇄ WEI** conversion for token precision handling.
63
+
64
+
65
+ ## Setup
66
+
67
+ Before installing this package, make sure you have an active **Payra** account:
68
+
69
+ [https://payra.cash/products/on-chain-payments/registration](https://payra.cash/products/on-chain-payments/registration#registration-form)
70
+
71
+ Before installing this package, make sure you have a **MerchantID**
72
+
73
+ - Your **Merchant ID** (unique for each blockchain network)
74
+ - Your **Signature Key** (used to sign Payra transactions securely)
75
+
76
+ Additionally:
77
+ To obtain your **RPC URLs** which are required for reading on-chain order statuses directly from the blockchain, you can use the public free endpoints provided with this package or create an account on one of the following services for better performance and reliability:
78
+
79
+ - **QuickNode** – Extremely fast and excellent for Polygon/Mainnet. ([quicknode.com](https://quicknode.com/))
80
+
81
+ - **Alchemy** – Offers a great developer dashboard and high reliability. ([alchemy.com](https://alchemy.com/))
82
+
83
+ - **DRPC** – Decentralized RPC with a generous free tier and a strict no-log policy. ([drpc.org](https://drpc.org))
84
+
85
+ - **Infura** – The industry standard; very stable, especially for Ethereum. ([infura.io](https://infura.io))
86
+
87
+ Optional (recommended):
88
+ - Create a free API key at [ExchangeRate API](https://www.exchangerate-api.com/) to enable **automatic fiat → USD conversions** using the built-in utility helpers.
89
+
90
+
91
+ ## Installation
92
+
93
+ ### From PyPI
94
+
95
+ Install the latest stable version from [PyPI](https://pypi.org/project/payra-sdk/):
96
+
97
+ ```bash
98
+ pip install payra-sdk
99
+ ```
100
+
101
+ ### From Source (for development)
102
+ Clone and install locally (editable mode for development):
103
+
104
+ ```bash
105
+ git clone https://github.com/payracash/payra-sdk-python.git
106
+ cd payra-sdk-python
107
+ python3 -m venv venv
108
+ source venv/bin/activate # Windows: venv\Scripts\activate
109
+ pip install -e .
110
+ ```
111
+
112
+ ### Dependencies
113
+
114
+ This SDK requires:
115
+ - **Python 3.8+**
116
+ - **web3.py**
117
+ - **ecdsa**
118
+ - **python-dotenv**
119
+ - _(optional)_ `requests` and `exchange-rate-api` for fiat conversion utilities.
120
+
121
+
122
+ ## Environment Configuration
123
+
124
+ Create a `.env` file in your project root (you can copy from example):
125
+
126
+ ```bash
127
+ cp .env.example .env
128
+ ```
129
+
130
+ This file stores your **private configuration** and connection settings for all supported networks. Never commit `.env` to version control.
131
+
132
+
133
+ ### Required Variables
134
+
135
+ #### Exchange Rate (optional)
136
+
137
+ Used for automatic fiat → USD conversions via the built-in Payra utilities.
138
+
139
+ ```bash
140
+ # Optional only needed if you want to use the built-in currency conversion helper
141
+ PAYRA_EXCHANGE_RATE_API_KEY= # Your ExchangeRate API key (from exchangerate-api.com)
142
+ PAYRA_EXCHANGE_RATE_CACHE_TIME=720 # Cache duration in minutes (default: 720 = 12h)
143
+
144
+ PAYRA_POLYGON_OCP_GATEWAY_CONTRACT_ADDRESS=0xc56c55D9cF0FF05c85A2DF5BFB9a65b34804063b
145
+ PAYRA_POLYGON_SIGNATURE_KEY=
146
+ PAYRA_POLYGON_MERCHANT_ID=
147
+ PAYRA_POLYGON_RPC_URL_1=https://polygon-rpc.com
148
+ PAYRA_POLYGON_RPC_URL_2=
149
+
150
+ PAYRA_ETHEREUM_OCP_GATEWAY_CONTRACT_ADDRESS=
151
+ PAYRA_ETHEREUM_SIGNATURE_KEY=
152
+ PAYRA_ETHEREUM_MERCHANT_ID=
153
+ PAYRA_ETHEREUM_RPC_URL_1=
154
+ PAYRA_ETHEREUM_RPC_URL_2=
155
+
156
+ PAYRA_LINEA_OCP_GATEWAY_CONTRACT_ADDRESS=
157
+ PAYRA_LINEA_SIGNATURE_KEY=
158
+ PAYRA_LINEA_MERCHANT_ID=
159
+ PAYRA_LINEA_RPC_URL_1=
160
+ PAYRA_LINEA_RPC_URL_2=
161
+ ```
162
+
163
+ #### Important Notes
164
+
165
+ - The cache automatically refreshes when it expires.
166
+ - You can adjust the cache duration by setting `PAYRA_EXCHANGE_RATE_CACHE_TIME`:
167
+ - `5` → cache for 5 minutes
168
+ - `60` → cache for 1 hour
169
+ - `720` → cache for 12 hours (default)
170
+ - Each network (Polygon, Ethereum, Linea) has its own **merchant ID**, **signature key**, and **RPC URLs**.
171
+ - The SDK automatically detects which chain configuration to use based on the selected network.
172
+ - You can use multiple RPC URLs for redundancy (the SDK will automatically fall back if one fails).
173
+ - Contract addresses correspond to the deployed Payra Core Forward contracts per network.
174
+
175
+
176
+ ## Usage Example
177
+
178
+ ### Generating and verifying a Payra signature in your backend
179
+
180
+ ```python
181
+ from payra_sdk import PayraUtils, PayraSignatureGenerator, PayraSDKException
182
+
183
+ try:
184
+ # Convert amount to smallest unit (wei or token decimals
185
+ amount_wei = PayraUtils.to_wei(3.34, 'polygon', 'usdt')
186
+
187
+ PAYMENT_DATA = {
188
+ "network": "polygon",
189
+ "tokenAddress": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", # USDT on Polygon
190
+ "orderId": "ORDER-1753824905006-301-322",
191
+ "amountWei": amount_wei, # e.g. 3.34 USDT in smallest unit
192
+ "timestamp": 1753826059, # current Unix timestamp
193
+ "payerAddress": "0xe6c961D6ad9a27Ea8e5d99e40abaC365DE9Cc162"
194
+ }
195
+
196
+ # Initialize signer
197
+ payra_signer = PayraSignatureGenerator()
198
+
199
+ # Generate cryptographic signature
200
+ signature = payra_signer.generate_signature(
201
+ network=PAYMENT_DATA["network"],
202
+ token_address=PAYMENT_DATA["tokenAddress"],
203
+ order_id=PAYMENT_DATA["orderId"],
204
+ amount_wei=PAYMENT_DATA["amountWei"],
205
+ timestamp=PAYMENT_DATA["timestamp"],
206
+ payer_address=PAYMENT_DATA["payerAddress"]
207
+ )
208
+
209
+ print(f"Generated signature: {signature}")
210
+ except PayraSDKException as e:
211
+ print(f"Payra SDK error: {e}")
212
+ except Exception as e:
213
+ print(f"Unexpected error: {e}")
214
+ ```
215
+
216
+ #### Input Parameters
217
+
218
+ | Field | Type | Description |
219
+ |--------------|----------|----------------------------------------------|
220
+ | **`network`** | `string` | Selected network name |
221
+ | **`tokenAddress`** | `string` | ERC20 token contract address |
222
+ | **`orderId`** | `string` | Unique order reference (e.g. ORDER-123) |
223
+ | **`amountWei`** | `string` or `integer` | Token amount in smallest unit (e.g. wei) |
224
+ | **`timestamp`** | `number` | Unix timestamp of signature creation |
225
+ | **`payerAddress`** | `string` | Payer Wallet Address
226
+
227
+ #### Behind the Scenes
228
+
229
+ 1. The backend converts the amount to the smallest blockchain unit (e.g. wei).
230
+ 2. A `PayraSignatureGenerator` instance is created using your signature key from `.env`
231
+ 3. It generates an ECDSA signature that is fully verifiable on-chain by the Payra smart contract.
232
+ 4. The resulting signature should be sent to the **frontend**, which must call `payOrder(...)` using the same parameters (`timestamp`, `orderId`, `amount`, `tokenAddress`, etc.) that were used to generate the signature.
233
+
234
+ ---
235
+
236
+ ### Get Order Status
237
+
238
+ Retrieve **full payment details** for a specific order from the Payra smart contract. This method returns the complete on-chain payment data associated with the order, including:
239
+ - whether the order has been paid,
240
+ - the payment token address,
241
+ - the paid amount,
242
+ - the fee amount,
243
+ - and the payment timestamp.
244
+
245
+ Use this method when you need **detailed information** about the payment or want to display full transaction data.
246
+
247
+ ```python
248
+ from payra_sdk import PayraOrderVerification, PayraSDKException
249
+
250
+ try:
251
+ ORDER_ID = "ord-258"
252
+ # Initialize verifier for a specific network
253
+ verifier = PayraOrderVerification("polygon")
254
+
255
+ print("\nGet order status...")
256
+ result = verifier.get_order_status(ORDER_ID)
257
+
258
+ print("Order ID:", ORDER_ID)
259
+ print("Result:", result)
260
+
261
+ except PayraSDKException as e:
262
+ print(f"Payra SDK error: {e}")
263
+ except Exception as e:
264
+ print(f"Unexpected error: {e}")
265
+ ```
266
+
267
+ #### Behind the Scenes
268
+
269
+ 1. The backend initializes a `PayraOrderVerification` object for the desired blockchain network.
270
+ 2. It calls `get_order_status(order_id)` to check if the order transaction exists and is confirmed on-chain.
271
+ 3. The function returns a dictionary with:
272
+
273
+ ```python
274
+ {
275
+ "success": True,
276
+ "paid": True,
277
+ "error": None.
278
+ "toke": '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
279
+ "amount": 400000,
280
+ "fee": 3600,
281
+ "timestamp": 1765138941
282
+ }
283
+ ```
284
+
285
+ ---
286
+
287
+ ### Check Order Paid Status
288
+
289
+ Perform a **simple payment check** for a specific order. This method only verifies whether the order has been paid (`true` or `false`) and does **not** return any additional payment details.
290
+
291
+ Use this method when you only need a **quick boolean confirmation** of the payment status.
292
+
293
+ ```python
294
+ from payra_sdk import PayraOrderVerification, PayraSDKException
295
+
296
+ try:
297
+ ORDER_ID = "ord-258"
298
+
299
+ # Initialize verifier for a specific network
300
+ verifier = PayraOrderVerification("polygon")
301
+
302
+ print("\nChecking order status...")
303
+ result = verifier.is_order_paid(ORDER_ID)
304
+
305
+ print("Order ID:", ORDER_ID)
306
+ print("Result:", result)
307
+
308
+ if result["success"] and result["paid"]:
309
+ print("Order is PAID on-chain")
310
+ elif result["success"]:
311
+ print("Order is NOT paid yet")
312
+ else:
313
+ print("Error:", result["error"])
314
+
315
+ except PayraSDKException as e:
316
+ print(f"Payra SDK error: {e}")
317
+ except Exception as e:
318
+ print(f"Unexpected error: {e}")
319
+ ```
320
+
321
+ #### Behind the Scenes
322
+
323
+ 1. The backend initializes a `PayraOrderVerification` object for the desired blockchain network.
324
+ 2. It calls `is_order_paid(order_id)` to check if the order transaction exists and is confirmed on-chain.
325
+ 3. The function returns a dictionary with:
326
+ ```python
327
+ {
328
+ "success": True,
329
+ "paid": True,
330
+ "error": None
331
+ }
332
+ ```
333
+ 4. If `paid` is `True`, the order has been successfully processed and confirmed by the Payra smart contract.
334
+
335
+ ---
336
+
337
+ ### Using Utility Functions
338
+
339
+ The `PayraUtils` module provides convenient helpers for token conversion, precision handling, and fiat currency operations.
340
+
341
+ ```python
342
+ from payra_sdk import PayraUtils
343
+
344
+ # Convert USD/token amount to smallest unit (Wei or token decimals)
345
+ amount_wei = PayraUtils.to_wei(3.34, 'polygon', 'usdt')
346
+ print("Amount in Wei:", amount_wei) # 3340000
347
+
348
+ # Convert from Wei back to readable token amount
349
+ amount = PayraUtils.from_wei(3340000, 'polygon', 'usdt', precision=2)
350
+ print("Readable amount:", amount) # "3.34"
351
+
352
+ # Get token decimals for any supported network
353
+ print("USDT decimals on Polygon:", PayraUtils.get_decimals("polygon", "usdt"))
354
+ print("POL decimals on Polygon:", PayraUtils.get_decimals("polygon", "pol"))
355
+
356
+ # Convert fiat currency to USD using the built-in ExchangeRate API
357
+ usd_value = PayraUtils.convert_to_usd(100, "EUR")
358
+ print(f"100 EUR = {usd_value} USD")
359
+ ```
360
+
361
+ #### Behind the Scenes
362
+
363
+ - `to_wei(amount, network, token)` – Converts a human-readable token amount into the smallest unit (used on-chain).
364
+ - `from_wei(amount, network, token, precision)` – Converts back from smallest unit to a formatted amount.
365
+ - `get_decimals(network, token)` – Returns the number of decimals for the given token on that network.
366
+ - `convert_to_usd(amount, currency)` – Converts fiat amounts (e.g. EUR, GBP) to USD using your ExchangeRate API key.
367
+
368
+
369
+ ## Testing
370
+
371
+ You can run the included `examples` to test signing and verification:
372
+
373
+ ```python
374
+ python3 example_signature.py
375
+ python3 example_order_verification.py
376
+ python3 example_utils.py
377
+ ```
378
+
379
+ Make sure your `.env` file contains correct values for the `network` being used.
380
+
381
+ ### Tips
382
+
383
+ - Always verify your `.env` configuration before running any signing or on-chain verification examples.
384
+ - The SDK examples are safe to run, they use **read-only RPC calls** (no real transactions are broadcast).
385
+ - You can modify `example_signature.py` to test custom token addresses or order parameters.
386
+
387
+
388
+ ## Projects
389
+
390
+ - [GitHub/Home](https://github.com/payracash)
391
+ - [GitHub/Source](https://github.com/payracash/payra-sdk-python)
392
+ - [GitHub/Issues](https://github.com/payracash/payra-sdk-python/issues)
393
+
394
+ ## Project
395
+
396
+ - [https://payra.cash](https://payra.cash)
397
+ - [https://payra.tech](https://payra.tech)
398
+ - [https://payra.xyz](https://payra.xyz)
399
+ - [https://payra.eth](https://payra.eth.limo) - suporrted by Brave and Opera Browser or .limo
400
+
401
+
402
+ ## Social Media
403
+
404
+ - [Telegram Payra Group](https://t.me/+GhTyJJrd4SMyMDA0)
405
+ - [Telegram Announcements](https://t.me/payracash)
406
+ - [Twix (X)](https://x.com/PayraCash)
407
+ - [Dev.to](https://dev.to/payracash)
408
+
409
+ ## License
410
+ MIT © [Payra](https://payra.cash)
@@ -0,0 +1,10 @@
1
+ payra_sdk/__init__.py,sha256=VMmHadXy0RLCjEPZhW_hCc3hg34pkmrL4VhTjqmVBiI,422
2
+ payra_sdk/exceptions.py,sha256=Dr8dy0RohAYII_-YeSu_doPJSr0EsJPcp6Oj02sio9Q,425
3
+ payra_sdk/order_verification.py,sha256=_AMdw4FQf-WMO0nswylW3AJoCJbxo7__Xi1zz6IeiJo,4342
4
+ payra_sdk/signature.py,sha256=JhozJ_Fwhn8lOHO-p8xEHn80I-TMHdeibzIXBI_bkkg,8627
5
+ payra_sdk/utils.py,sha256=mp9xX-6ph_xRMSIHh6PP41RcYyKZMLVPEi9M3IxxC4c,4147
6
+ payra_sdk-1.2.6.dist-info/licenses/LICENSE,sha256=mnnujAcvHr1ULEDD2l0ZT6lrpeTv_9bG3n8sngv7ew8,120
7
+ payra_sdk-1.2.6.dist-info/METADATA,sha256=e3OjC4MBEExh-3BVtQvoYznj1J-oSbgXfC6x54h8Kas,14365
8
+ payra_sdk-1.2.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
+ payra_sdk-1.2.6.dist-info/top_level.txt,sha256=oC4dhGzjcpDRRwyU9JjtLu56Uc5moaa76utQGYsPL-g,10
10
+ payra_sdk-1.2.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,350 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: payra_sdk
3
- Version: 1.2.4
4
- Summary: Python SDK for Payra payment signature generation (backend)
5
- Author: Your Name
6
- Author-email: Wraith <contact@payra.cash>
7
- License: MIT License
8
-
9
- Copyright (c) 2025 Wraith
10
-
11
- Permission is hereby granted, free of charge, to any person obtaining a copy...
12
-
13
- Project-URL: Homepage, https://github.com/payracash
14
- Project-URL: Source, https://github.com/payracash/payra-sdk-python
15
- Project-URL: Issues, https://github.com/payracash/payra-sdk-python/issues
16
- Requires-Python: >=3.8
17
- Description-Content-Type: text/markdown
18
- License-File: LICENSE
19
- Requires-Dist: web3>=6.0.0
20
- Requires-Dist: eth-abi>=4.0.0
21
- Requires-Dist: eth-account>=0.10.0
22
- Requires-Dist: python-dotenv>=1.0.0
23
- Dynamic: license-file
24
-
25
-
26
-
27
- # Payra Python SDK
28
-
29
- Official **Python SDK** for integrating **Payra's on-chain payment system** into your backend applications.
30
-
31
- This SDK provides:
32
- - Secure generation of **ECDSA signatures** compatible with the Payra smart contract — used for order payment verification.
33
- - Simple methods for **checking the on-chain status of orders** to confirm completed payments.
34
-
35
- ## How It Works
36
-
37
- The typical flow for signing and verifying a Payra transaction:
38
-
39
- 1. The **frontend** prepares all required payment parameters:
40
- - **Network** – blockchain name (e.g. Polygon, Linea)
41
- - **Token address** – ERC-20 token contract address
42
- - **Order ID** – unique order identifier
43
- - **Amount Wei** – already converted to the smallest unit (e.g. wei, 10⁶)
44
- - **Timestamp** – Unix timestamp of the order
45
- - **Payer wallet address** – the wallet address from which the user will make the on-chain payment
46
- 2. The frontend sends these parameters to your **backend**.
47
- 3. The **backend** uses this SDK to generate a cryptographic **ECDSA signature** with its private key (performed **offline**).
48
- 4. The backend returns the generated signature to the frontend.
49
- 5. The **frontend** calls the Payra smart contract (`payOrder`) with all parameters **plus** the signature.
50
-
51
- This process ensures full compatibility between your backend and Payra’s on-chain verification logic.
52
-
53
- ## Features
54
-
55
- - Generates **Ethereum ECDSA signatures** using the `secp256k1` curve.
56
- - Fully compatible with **Payra's Solidity smart contracts** (`ERC-1155` payment verification).
57
- - Includes built-in **ABI encoding and decoding** via `web3.php`.
58
- - Supports `.env` and `config/payra.php` configuration for multiple blockchain networks.
59
- - Laravel IoC container integration (easy dependency injection)
60
- - Verifies **order payment status directly on-chain** via RPC or blockchain explorer API.
61
- - Provides **secure backend integration** for signing and verifying transactions.
62
- - Includes optional utility helpers for:
63
- - **Currency conversion** (via [ExchangeRate API](https://www.exchangerate-api.com/))
64
- - **USD ⇄ WEI** conversion for token precision handling.
65
-
66
- ## Setup
67
-
68
- Before installing this package, make sure you have an active **Payra** account:
69
-
70
- [https://payra.cash](https://payra.cash)
71
-
72
- You will need:
73
-
74
- - Your **Merchant ID** (unique for each blockchain network)
75
- - Your **Private Key** (used to sign Payra transactions securely)
76
-
77
- Additionally:
78
-
79
- - Create a free account at [QuickNode](https://www.quicknode.com/) to obtain your **RPC URLs** - these are required for reading on-chain order statuses directly from the blockchain.
80
-
81
- Optional (recommended):
82
-
83
- - Create a free API key at [ExchangeRate API](https://www.exchangerate-api.com/)
84
- to enable **automatic fiat → USD conversions** using the built-in utility helpers.
85
-
86
- ## Installation
87
-
88
- ### From PyPI
89
-
90
- Install the latest stable version from [PyPI](https://pypi.org/project/payra-sdk/):
91
-
92
- ```bash
93
- pip install payra-sdk
94
- ```
95
-
96
- ### From Source (for development)
97
-
98
- Clone and install locally (editable mode for development):
99
-
100
- ```bash
101
- git clone https://github.com/payracash/payra-sdk-python.git
102
- cd payra-sdk-python
103
-
104
- python3 -m venv venv
105
- source venv/bin/activate # Windows: venv\Scripts\activate
106
-
107
- pip install -e .
108
- ```
109
-
110
- ### Dependencies
111
-
112
- This SDK requires:
113
- - **Python 3.8+**
114
- - **web3.py**
115
- - **ecdsa**
116
- - **python-dotenv**
117
- - _(optional)_ `requests` and `exchange-rate-api` for fiat conversion utilities.
118
-
119
- ## Environment Configuration
120
-
121
- Create a `.env` file in your project root (you can copy from example):
122
-
123
- ```bash
124
- cp .env.example .env
125
- ```
126
-
127
- This file stores your **private configuration** and connection settings for all supported networks. Never commit `.env` to version control.
128
-
129
- ### Required Variables
130
-
131
- #### Exchange Rate (optional)
132
-
133
- Used for automatic fiat → USD conversions via the built-in Payra utilities.
134
-
135
- ```bash
136
- # Optional — only needed if you want to use the built-in currency conversion helper
137
- PAYRA_EXCHANGE_RATE_API_KEY= # Your ExchangeRate API key (from exchangerate-api.com)
138
- PAYRA_EXCHANGE_RATE_CACHE_TIME=720 # Cache duration in minutes (default: 720 = 12h)
139
-
140
- PAYRA_POLYGON_CORE_FORWARD_CONTRACT_ADDRESS=0xf30070da76B55E5cB5750517E4DECBD6Cc5ce5a8
141
- PAYRA_POLYGON_PRIVATE_KEY=
142
- PAYRA_POLYGON_MERCHANT_ID=
143
- PAYRA_POLYGON_RPC_URL_1=
144
- PAYRA_POLYGON_RPC_URL_2=
145
-
146
- PAYRA_ETHEREUM_CORE_FORWARD_CONTRACT_ADDRESS=
147
- PAYRA_ETHEREUM_PRIVATE_KEY=
148
- PAYRA_ETHEREUM_MERCHANT_ID=
149
- PAYRA_ETHEREUM_RPC_URL_1=
150
- PAYRA_ETHEREUM_RPC_URL_2=
151
-
152
- PAYRA_LINEA_CORE_FORWARD_CONTRACT_ADDRESS=
153
- PAYRA_LINEA_PRIVATE_KEY=
154
- PAYRA_LINEA_MERCHANT_ID=
155
- PAYRA_LINEA_RPC_URL_1=
156
- PAYRA_LINEA_RPC_URL_2=
157
- ```
158
-
159
- #### Important Notes
160
-
161
- - The cache automatically refreshes when it expires.
162
- - You can adjust the cache duration by setting `PAYRA_EXCHANGE_RATE_CACHE_TIME`:
163
- - `5` → cache for 5 minutes
164
- - `60` → cache for 1 hour
165
- - `720` → cache for 12 hours (default)
166
- - Each network (Polygon, Ethereum, Linea) has its own **merchant ID**, **private key**, and **RPC URLs**.
167
- - The SDK automatically detects which chain configuration to use based on the selected network.
168
- - You can use multiple RPC URLs for redundancy (the SDK will automatically fall back if one fails).
169
- - Contract addresses correspond to the deployed Payra Core Forward contracts per network.
170
-
171
- ## Usage Example
172
-
173
- ### Generating and verifying a Payra signature in your backend
174
-
175
- ```python
176
- from payra_sdk import PayraUtils, PayraSignatureGenerator, PayraSDKException
177
-
178
- try:
179
- # Convert amount to smallest unit (wei or token decimals)
180
- amount_wei = PayraUtils.to_wei(3.34, 'polygon', 'usdt')
181
-
182
- PAYMENT_DATA = {
183
- "network": "polygon",
184
- "tokenAddress": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", # USDT on Polygon
185
- "orderId": "ORDER-1753824905006-301-322",
186
- "amountWei": amount_wei, # e.g. 3.34 USDT in smallest unit
187
- "timestamp": 1753826059, # current Unix timestamp
188
- "payerAddress": "0xe6c961D6ad9a27Ea8e5d99e40abaC365DE9Cc162"
189
- }
190
-
191
- # Initialize signer
192
- payra_signer = PayraSignatureGenerator()
193
-
194
- # Generate cryptographic signature
195
- signature = payra_signer.generate_signature(
196
- network=PAYMENT_DATA["network"],
197
- token_address=PAYMENT_DATA["tokenAddress"],
198
- order_id=PAYMENT_DATA["orderId"],
199
- amount_wei=PAYMENT_DATA["amountWei"],
200
- timestamp=PAYMENT_DATA["timestamp"],
201
- payer_address=PAYMENT_DATA["payerAddress"]
202
- )
203
-
204
- print(f"Generated signature: {signature}")
205
-
206
- except PayraSDKException as e:
207
- print(f"Payra SDK error: {e}")
208
- except Exception as e:
209
- print(f"Unexpected error: {e}")
210
- ```
211
-
212
- #### Input Parameters
213
-
214
- | Field | Type | Description |
215
- |--------------|----------|----------------------------------------------|
216
- | **`network`** | `string` | Selected network name |
217
- | **`tokenAddress`** | `string` | ERC20 token contract address |
218
- | **`orderId`** | `string` | Unique order reference (e.g. ORDER-123) |
219
- | **`amountWei`** | `string` or `integer` | Token amount in smallest unit (e.g. wei) |
220
- | **`timestamp`** | `number` | Unix timestamp of signature creation |
221
- | **`payerAddress`** | `string` | Payer Wallet Address
222
-
223
- #### Behind the Scenes
224
-
225
- 1. The backend converts the amount to the smallest blockchain unit (e.g. wei).
226
- 3. A `PayraSignatureGenerator` instance is created using your private key from `.env`
227
- 4. It generates an ECDSA signature that is fully verifiable on-chain by the Payra smart contract.
228
- 5. The resulting signature should be sent to the **frontend**, which must call `payOrder(...)` using the same parameters (`timestamp`, `orderId`, `amount`, `tokenAddress`, etc.) that were used to generate the signature.
229
-
230
- ---
231
-
232
- ### Checking On-Chain Order Status
233
-
234
- You can verify whether a Payra order has been successfully paid on-chain:
235
-
236
- ```python
237
- from payra_sdk import PayraOrderVerification, PayraSDKException
238
-
239
- try:
240
- ORDER_ID = "ORDER-1753824905006-301-322"
241
-
242
- # Initialize verifier for a specific network
243
- verifier = PayraOrderVerification("polygon")
244
-
245
- print("\nChecking order status...")
246
- result = verifier.is_order_paid(ORDER_ID)
247
-
248
- print("Order ID:", ORDER_ID)
249
- print("Result:", result)
250
-
251
- if result["success"] and result["paid"]:
252
- print("Order is PAID on-chain")
253
- elif result["success"]:
254
- print("Order is NOT paid yet")
255
- else:
256
- print("Error:", result["error"])
257
-
258
- except PayraSDKException as e:
259
- print(f"Payra SDK error: {e}")
260
- except Exception as e:
261
- print(f"Unexpected error: {e}")
262
- ```
263
-
264
- #### Behind the Scenes
265
-
266
- 1. The backend initializes a `PayraOrderVerification` object for the desired blockchain network.
267
- 3. It calls `is_order_paid(order_id)` to check if the order transaction exists and is confirmed on-chain.
268
- 4. The function returns a dictionary with:
269
- ```python
270
- {
271
- "success": True,
272
- "paid": True,
273
- "error": None
274
- }
275
- ```
276
- 5. If `paid` is `True`, the order has been successfully processed and confirmed by the Payra smart contract.
277
-
278
- ---
279
-
280
- ### Using Utility Functions
281
-
282
- The `PayraUtils` module provides convenient helpers for token conversion, precision handling, and fiat currency operations.
283
-
284
- ```python
285
- from payra_sdk import PayraUtils
286
-
287
- # 🔹 Convert USD/token amount to smallest unit (Wei or token decimals)
288
- amount_wei = PayraUtils.to_wei(3.34, 'polygon', 'usdt')
289
- print("Amount in Wei:", amount_wei) # 3340000
290
-
291
- # 🔹 Convert from Wei back to readable token amount
292
- amount = PayraUtils.from_wei(3340000, 'polygon', 'usdt', precision=2)
293
- print("Readable amount:", amount) # "3.34"
294
-
295
- # 🔹 Get token decimals for any supported network
296
- print("USDT decimals on Polygon:", PayraUtils.get_decimals("polygon", "usdt"))
297
- print("POL decimals on Polygon:", PayraUtils.get_decimals("polygon", "pol"))
298
-
299
- # 🔹 Convert fiat currency to USD using the built-in ExchangeRate API
300
- usd_value = PayraUtils.convert_to_usd(100, "EUR")
301
- print(f"100 EUR = {usd_value} USD")
302
- ```
303
-
304
- #### Behind the Scenes
305
-
306
- - `to_wei(amount, network, token)` – Converts a human-readable token amount into the smallest unit (used on-chain).
307
- - `from_wei(amount, network, token, precision)` – Converts back from smallest unit to a formatted amount.
308
- - `get_decimals(network, token)` – Returns the number of decimals for the given token on that network.
309
- - `convert_to_usd(amount, currency)` – Converts fiat amounts (e.g. EUR, GBP) to USD using your ExchangeRate API key.
310
-
311
- ## Testing
312
- You can run the included `examples` to test signing and verification:
313
-
314
- ```python
315
- python3 example_signature.py
316
- python3 example_order_verification.py
317
- python3 example_utils.py
318
- ```
319
-
320
- Make sure your `.env` file contains correct values for the `network` being used.
321
-
322
- ### Tips
323
-
324
- - Always verify your `.env` configuration before running any signing or on-chain verification examples.
325
- - The SDK examples are safe to run — they use **read-only RPC calls** (no real transactions are broadcast).
326
- - You can modify `example_signature.py` to test custom token addresses or order parameters.
327
-
328
- ## Projects
329
-
330
- - [GitHub / Home](https://github.com/payracash)
331
- - [GitHub / Source](https://github.com/payracash/payra-sdk-python)
332
- - [GitHub / Issues](https://github.com/payracash/payra-sdk-python/issues)
333
-
334
- ## Project
335
-
336
- - [https://payra.cash](https://payra.cash)
337
- - [https://payra.tech](https://payra.tech)
338
- - [https://payra.xyz](https://payra.xyz)
339
- - [https://payra.eth](https://payra.eth)
340
-
341
- ## Social Media
342
-
343
- - [Telegram Payra Group](https://t.me/+GhTyJJrd4SMyMDA0)
344
- - [Telegram Announcements](https://t.me/payracash)
345
- - [Twix (X)](https://x.com/PayraCash)
346
- - [Hashnode](https://payra.hashnode.dev)
347
-
348
- ## License
349
-
350
- MIT © [Payra](https://github.com/payracash)
@@ -1,10 +0,0 @@
1
- payra_sdk/__init__.py,sha256=VMmHadXy0RLCjEPZhW_hCc3hg34pkmrL4VhTjqmVBiI,422
2
- payra_sdk/exceptions.py,sha256=Dr8dy0RohAYII_-YeSu_doPJSr0EsJPcp6Oj02sio9Q,425
3
- payra_sdk/order_verification.py,sha256=IWKV4V6gZRyrtJtXsDRPflUcSsuN5D1kFZn5QVTPi5Q,3379
4
- payra_sdk/signature.py,sha256=Vn85w4DAGzHK1y9h3hMETij_-Qa4aSNz0KwnzwCyFQw,8613
5
- payra_sdk/utils.py,sha256=gIQZX9N97Eneh5ScJ-YWY8_8wRqxBe0h5lDAVFYV8CE,4854
6
- payra_sdk-1.2.4.dist-info/licenses/LICENSE,sha256=mnnujAcvHr1ULEDD2l0ZT6lrpeTv_9bG3n8sngv7ew8,120
7
- payra_sdk-1.2.4.dist-info/METADATA,sha256=BF0bsVhP4ycHdUfzYPG1q6c6zR_z5aG2i2toHAGRW2c,12574
8
- payra_sdk-1.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- payra_sdk-1.2.4.dist-info/top_level.txt,sha256=oC4dhGzjcpDRRwyU9JjtLu56Uc5moaa76utQGYsPL-g,10
10
- payra_sdk-1.2.4.dist-info/RECORD,,