paynode-sdk-python 1.1.2__py3-none-any.whl → 1.1.3__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.
paynode_sdk/client.py CHANGED
@@ -46,7 +46,7 @@ class PayNodeAgentClient:
46
46
  except Exception as e:
47
47
  logger.warning(f"⚠️ [PayNode-PY] RPC {rpc} failed: {str(e)}")
48
48
  continue
49
- raise PayNodeException("Failed to connect to any provided RPC nodes.", ErrorCode.RPC_ERROR)
49
+ raise PayNodeException("Failed to connect to any provided RPC nodes.", ErrorCode.rpc_error)
50
50
 
51
51
  def request_gate(self, url: str, method: str = "GET", **kwargs):
52
52
  """The high-level autonomous method handling 402 loop."""
@@ -67,7 +67,7 @@ class PayNodeAgentClient:
67
67
  kwargs = self._handle_402(response.headers, **kwargs)
68
68
  except Exception as e:
69
69
  if isinstance(e, PayNodeException): raise
70
- raise PayNodeException(f"An unexpected error occurred: {str(e)}", ErrorCode.INTERNAL_ERROR)
70
+ raise PayNodeException(f"An unexpected error occurred: {str(e)}", ErrorCode.internal_error)
71
71
  continue
72
72
  return response
73
73
  return response
@@ -80,11 +80,11 @@ class PayNodeAgentClient:
80
80
  order_id = headers.get('x-paynode-order-id')
81
81
 
82
82
  if not all([router_addr, merchant_addr, amount_raw, token_addr, order_id]):
83
- raise PayNodeException("Malformed 402 headers: missing metadata", ErrorCode.INTERNAL_ERROR)
83
+ raise PayNodeException("Malformed 402 headers: missing metadata", ErrorCode.internal_error)
84
84
 
85
85
  # v1.3 Constraint: Min payment protection
86
86
  if amount_raw < 1000:
87
- raise PayNodeException("Payment amount is below the protocol minimum (1000).", ErrorCode.AMOUNT_TOO_LOW)
87
+ raise PayNodeException("Payment amount is below the protocol minimum (1000).", ErrorCode.amount_too_low)
88
88
 
89
89
  # Protocol v1.3: Permit-First Execution
90
90
  try:
@@ -99,7 +99,7 @@ class PayNodeAgentClient:
99
99
  logger.info(f"✅ [PayNode-PY] Payment successful: {tx_hash}")
100
100
  except Exception as e:
101
101
  if isinstance(e, PayNodeException): raise
102
- raise PayNodeException(f"On-chain transaction reverted or failed: {str(e)}", ErrorCode.TRANSACTION_FAILED)
102
+ raise PayNodeException(f"On-chain transaction reverted or failed: {str(e)}", ErrorCode.transaction_failed)
103
103
 
104
104
  retry_headers = kwargs.get('headers', {}).copy()
105
105
  retry_headers.update({'x-paynode-receipt': tx_hash, 'x-paynode-order-id': order_id})
@@ -166,7 +166,12 @@ class PayNodeAgentClient:
166
166
  }
167
167
 
168
168
  signed = self.account.sign_typed_data(full_message=structured_data)
169
- return {"v": signed.v, "r": signed.r, "s": signed.s, "deadline": deadline}
169
+ return {
170
+ "v": signed.v,
171
+ "r": Web3.to_bytes(signed.r).rjust(32, b'\0'),
172
+ "s": Web3.to_bytes(signed.s).rjust(32, b'\0'),
173
+ "deadline": deadline
174
+ }
170
175
 
171
176
  def pay_with_permit_auto(self, router_addr, token_addr, merchant_addr, amount, order_id):
172
177
  """Combines sign_permit and on-chain submission."""
paynode_sdk/errors.py CHANGED
@@ -2,26 +2,18 @@ from enum import Enum
2
2
  from typing import Any, Optional
3
3
 
4
4
  class ErrorCode(str, Enum):
5
- # Authentication & Receipts
6
- MISSING_RECEIPT = 'PAYNODE_MISSING_RECEIPT'
7
- INVALID_RECEIPT = 'PAYNODE_INVALID_RECEIPT'
8
- RECEIPT_ALREADY_USED = 'PAYNODE_RECEIPT_ALREADY_USED'
9
- TRANSACTION_NOT_FOUND = 'PAYNODE_TRANSACTION_NOT_FOUND'
10
- TRANSACTION_FAILED = 'PAYNODE_TRANSACTION_FAILED'
11
-
12
- # Validation
13
- WRONG_CONTRACT = 'PAYNODE_WRONG_CONTRACT'
14
- WRONG_MERCHANT = 'PAYNODE_WRONG_MERCHANT'
15
- WRONG_TOKEN = 'PAYNODE_WRONG_TOKEN'
16
- TOKEN_NOT_ACCEPTED = 'PAYNODE_TOKEN_NOT_ACCEPTED'
17
- AMOUNT_TOO_LOW = 'PAYNODE_AMOUNT_TOO_LOW'
18
- INSUFFICIENT_FUNDS = 'PAYNODE_INSUFFICIENT_FUNDS'
19
- ORDER_MISMATCH = 'PAYNODE_ORDER_MISMATCH'
20
- PERMIT_FAILED = 'PAYNODE_PERMIT_FAILED'
21
-
22
- # System
23
- RPC_ERROR = 'PAYNODE_RPC_ERROR'
24
- INTERNAL_ERROR = 'PAYNODE_INTERNAL_ERROR'
5
+ rpc_error = 'rpc_error'
6
+ insufficient_funds = 'insufficient_funds'
7
+ amount_too_low = 'amount_too_low'
8
+ token_not_accepted = 'token_not_accepted'
9
+ transaction_failed = 'transaction_failed'
10
+ duplicate_transaction = 'duplicate_transaction'
11
+ invalid_receipt = 'invalid_receipt'
12
+ internal_error = 'internal_error'
13
+ transaction_not_found = 'transaction_not_found'
14
+ wrong_contract = 'wrong_contract'
15
+ order_mismatch = 'order_mismatch'
16
+ missing_receipt = 'missing_receipt'
25
17
 
26
18
  class PayNodeException(Exception):
27
19
  def __init__(self, message: str, code: ErrorCode, details: Optional[Any] = None):
paynode_sdk/middleware.py CHANGED
@@ -61,7 +61,7 @@ class PayNodeMiddleware(BaseHTTPMiddleware):
61
61
  headers=headers,
62
62
  content={
63
63
  "error": "Payment Required",
64
- "code": ErrorCode.MISSING_RECEIPT,
64
+ "code": ErrorCode.missing_receipt,
65
65
  "message": "Please pay to PayNode contract and provide 'x-paynode-receipt' header.",
66
66
  "amount": self.price,
67
67
  "currency": self.currency
@@ -87,7 +87,7 @@ class PayNodeMiddleware(BaseHTTPMiddleware):
87
87
  status_code=403,
88
88
  content={
89
89
  "error": "Forbidden",
90
- "code": err.code if hasattr(err, 'code') else ErrorCode.INVALID_RECEIPT,
90
+ "code": err.code if hasattr(err, 'code') else ErrorCode.invalid_receipt,
91
91
  "message": str(err)
92
92
  }
93
93
  )
paynode_sdk/verifier.py CHANGED
@@ -17,7 +17,7 @@ class PayNodeVerifier:
17
17
  except Exception:
18
18
  continue
19
19
  if not self.w3:
20
- raise PayNodeException("Failed to connect to any provided RPC nodes.", ErrorCode.RPC_ERROR)
20
+ raise PayNodeException("Failed to connect to any provided RPC nodes.", ErrorCode.rpc_error)
21
21
  self.contract_address = contract_address
22
22
  self.chain_id = int(chain_id) if chain_id else None
23
23
  self.store = store or MemoryIdempotencyStore()
@@ -34,14 +34,14 @@ class PayNodeVerifier:
34
34
 
35
35
  async def verify_payment(self, tx_hash, expected):
36
36
  if not self.w3:
37
- return {"isValid": False, "error": PayNodeException("Verifier Provider Missing", ErrorCode.RPC_ERROR)}
37
+ return {"isValid": False, "error": PayNodeException("Verifier Provider Missing", ErrorCode.rpc_error)}
38
38
 
39
39
  # 0. Dust Exploit Check (Minimum Payment)
40
40
  amount = int(expected.get("amount", 0))
41
41
  if amount < MIN_PAYMENT_AMOUNT:
42
42
  return {"isValid": False, "error": PayNodeException(
43
43
  f"Payment amount {amount} is below the minimum threshold of {MIN_PAYMENT_AMOUNT}.",
44
- ErrorCode.AMOUNT_TOO_LOW
44
+ ErrorCode.amount_too_low
45
45
  )}
46
46
 
47
47
  # 1. Token Whitelist Check (Anti-FakeToken)
@@ -49,39 +49,39 @@ class PayNodeVerifier:
49
49
  if self.accepted_tokens and expected_token not in self.accepted_tokens:
50
50
  return {"isValid": False, "error": PayNodeException(
51
51
  f"Token {expected.get('tokenAddress')} is not in the accepted whitelist.",
52
- ErrorCode.TOKEN_NOT_ACCEPTED
52
+ ErrorCode.token_not_accepted
53
53
  )}
54
54
 
55
55
  try:
56
56
  is_new = await self.store.check_and_set(tx_hash, 86400) # 24 hour TTL
57
57
  if not is_new:
58
- return {"isValid": False, "error": PayNodeException("Receipt already used", ErrorCode.RECEIPT_ALREADY_USED)}
58
+ return {"isValid": False, "error": PayNodeException("Receipt already used", ErrorCode.receipt_already_used)}
59
59
  except Exception as e:
60
- return {"isValid": False, "error": PayNodeException("Store Error", ErrorCode.INTERNAL_ERROR, details=str(e))}
60
+ return {"isValid": False, "error": PayNodeException("Store Error", ErrorCode.internal_error, details=str(e))}
61
61
 
62
62
  try:
63
63
  receipt = self.w3.eth.get_transaction_receipt(tx_hash)
64
64
  except Exception:
65
- return {"isValid": False, "error": PayNodeException("Transaction not found", ErrorCode.TRANSACTION_NOT_FOUND)}
65
+ return {"isValid": False, "error": PayNodeException("Transaction not found", ErrorCode.transaction_not_found)}
66
66
 
67
67
  if not receipt:
68
- return {"isValid": False, "error": PayNodeException("Transaction not found", ErrorCode.TRANSACTION_NOT_FOUND)}
68
+ return {"isValid": False, "error": PayNodeException("Transaction not found", ErrorCode.transaction_not_found)}
69
69
 
70
70
  if receipt.get("status") == 0:
71
- return {"isValid": False, "error": PayNodeException("Transaction failed", ErrorCode.TRANSACTION_FAILED)}
71
+ return {"isValid": False, "error": PayNodeException("Transaction failed", ErrorCode.transaction_failed)}
72
72
 
73
73
  if not receipt.get("to") or receipt.get("to", "").lower() != self.contract_address.lower():
74
- return {"isValid": False, "error": PayNodeException("Wrong contract", ErrorCode.WRONG_CONTRACT)}
74
+ return {"isValid": False, "error": PayNodeException("Wrong contract", ErrorCode.wrong_contract)}
75
75
 
76
76
  contract = self.w3.eth.contract(address=Web3.to_checksum_address(self.contract_address), abi=PAYNODE_ROUTER_ABI)
77
77
 
78
78
  try:
79
79
  logs = contract.events.PaymentReceived().process_receipt(receipt)
80
80
  except Exception:
81
- return {"isValid": False, "error": PayNodeException("Invalid receipt format", ErrorCode.INVALID_RECEIPT)}
81
+ return {"isValid": False, "error": PayNodeException("Invalid receipt format", ErrorCode.invalid_receipt)}
82
82
 
83
83
  if not logs:
84
- return {"isValid": False, "error": PayNodeException("No valid PaymentReceived event found", ErrorCode.INVALID_RECEIPT)}
84
+ return {"isValid": False, "error": PayNodeException("No valid PaymentReceived event found", ErrorCode.invalid_receipt)}
85
85
 
86
86
  # Find the valid log
87
87
  merchant = expected.get("merchantAddress", "").lower()
@@ -113,6 +113,6 @@ class PayNodeVerifier:
113
113
  break
114
114
 
115
115
  if not valid:
116
- return {"isValid": False, "error": PayNodeException("Payment criteria mismatch", ErrorCode.INVALID_RECEIPT)}
116
+ return {"isValid": False, "error": PayNodeException("Payment criteria mismatch", ErrorCode.invalid_receipt)}
117
117
 
118
118
  return {"isValid": True}
paynode_sdk/webhook.py CHANGED
@@ -213,7 +213,7 @@ class PayNodeWebhookNotifier:
213
213
  if resp.status >= 400:
214
214
  raise PayNodeException(
215
215
  f"Webhook returned {resp.status}",
216
- ErrorCode.INTERNAL_ERROR
216
+ ErrorCode.internal_error
217
217
  )
218
218
 
219
219
  logger.info(f"✅ [PayNode Webhook] Delivered tx {event.tx_hash[:10]}... → {resp.status}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paynode-sdk-python
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: PayNode Protocol Python SDK for AI Agents
5
5
  Author-email: PayNodeLabs <contact@paynode.dev>
6
6
  License: MIT
@@ -0,0 +1,13 @@
1
+ paynode_sdk/__init__.py,sha256=p6URBqxFz1AErG7rIRWKnfwnzAC19qrMURtG7YhVR1I,821
2
+ paynode_sdk/client.py,sha256=tNQE3dizUnIc15Z9M1NU_0jZcoLJe01oN1TI40Ynl2k,10761
3
+ paynode_sdk/constants.py,sha256=puqz09qeKcMoigWrqdIzkhtAzFpECxEwHGLvDd3U_CQ,1429
4
+ paynode_sdk/errors.py,sha256=m49zByht9_nrmgX4_zHUR5eLsCHX_iR-d_BJZ2VNOx8,829
5
+ paynode_sdk/idempotency.py,sha256=od7HuSxFdejBP0oE4QCzbJdrDZWvziiu09d3BRErU2k,999
6
+ paynode_sdk/middleware.py,sha256=ubSFv2fiAjJ16cxrORDfWbsUHffhBbD4Wfvt0zM7lvE,3563
7
+ paynode_sdk/verifier.py,sha256=CzWO9IsXwc3AeCkmXy8gvcNTUbPhXnbfxmAa7lr1mhE,5274
8
+ paynode_sdk/webhook.py,sha256=DaCIuZ_rI7Kynt60Drw2EKQJuhNW0GoMk_u9EZy4Jxs,8302
9
+ paynode_sdk_python-1.1.3.dist-info/licenses/LICENSE,sha256=U8RjGlEBtXN6PA-qN_N3Uh60jyu3qe26ZBmgt-LAHc4,1069
10
+ paynode_sdk_python-1.1.3.dist-info/METADATA,sha256=nluXsilwB805frjjWE2rKgJ_ZC98hjQK_06O4HGSIao,2858
11
+ paynode_sdk_python-1.1.3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
12
+ paynode_sdk_python-1.1.3.dist-info/top_level.txt,sha256=c6Skc1Xx-9O-JJ7sHghLW8Kyn4hyJoVPUawH1Mu8iTU,12
13
+ paynode_sdk_python-1.1.3.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- paynode_sdk/__init__.py,sha256=p6URBqxFz1AErG7rIRWKnfwnzAC19qrMURtG7YhVR1I,821
2
- paynode_sdk/client.py,sha256=rwn6bTSyIuIXd8aLnK5Nnd7R5p8Hx25ETzfNQil6y1k,10639
3
- paynode_sdk/constants.py,sha256=puqz09qeKcMoigWrqdIzkhtAzFpECxEwHGLvDd3U_CQ,1429
4
- paynode_sdk/errors.py,sha256=GN0qycgjATT47dF4yZ6Ulg18jf2OwholvYl0pgCvuzo,1121
5
- paynode_sdk/idempotency.py,sha256=od7HuSxFdejBP0oE4QCzbJdrDZWvziiu09d3BRErU2k,999
6
- paynode_sdk/middleware.py,sha256=KAcSVaVyApO4evby4vnv1erifqi3VzSBFbWelGGSF_w,3563
7
- paynode_sdk/verifier.py,sha256=V7VXWqXzkObfN9v9agb7CWiLLwKbX1f2gsIWQE0qDdk,5274
8
- paynode_sdk/webhook.py,sha256=KxlXGkXe3wpR8ueO8FMW2iypJgWaLwMfxAAZLNRvNDo,8302
9
- paynode_sdk_python-1.1.2.dist-info/licenses/LICENSE,sha256=U8RjGlEBtXN6PA-qN_N3Uh60jyu3qe26ZBmgt-LAHc4,1069
10
- paynode_sdk_python-1.1.2.dist-info/METADATA,sha256=_qVjPReImedgxEqo1w4FlZVgead-vXFxAGvR3nZVyx4,2858
11
- paynode_sdk_python-1.1.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
12
- paynode_sdk_python-1.1.2.dist-info/top_level.txt,sha256=c6Skc1Xx-9O-JJ7sHghLW8Kyn4hyJoVPUawH1Mu8iTU,12
13
- paynode_sdk_python-1.1.2.dist-info/RECORD,,