phala-pay 0.2.0__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.
Files changed (193) hide show
  1. phala_pay/__init__.py +96 -0
  2. phala_pay/_client.py +582 -0
  3. phala_pay/_types.py +42 -0
  4. phala_pay/_webhook.py +174 -0
  5. phala_pay/py.typed +0 -0
  6. phala_pay-0.2.0.dist-info/METADATA +204 -0
  7. phala_pay-0.2.0.dist-info/RECORD +193 -0
  8. phala_pay-0.2.0.dist-info/WHEEL +4 -0
  9. phala_pay-0.2.0.dist-info/entry_points.txt +2 -0
  10. phala_pay-0.2.0.dist-info/licenses/LICENSE +202 -0
  11. topup_client/__init__.py +8 -0
  12. topup_client/api/__init__.py +1 -0
  13. topup_client/api/account/__init__.py +1 -0
  14. topup_client/api/account/get_account.py +142 -0
  15. topup_client/api/account/pause_account.py +212 -0
  16. topup_client/api/account/resume_account.py +208 -0
  17. topup_client/api/account/roll_webhook_key.py +237 -0
  18. topup_client/api/account/update_account.py +220 -0
  19. topup_client/api/api_keys/__init__.py +1 -0
  20. topup_client/api/api_keys/create_api_key.py +229 -0
  21. topup_client/api/api_keys/get_api_key.py +173 -0
  22. topup_client/api/api_keys/list_api_keys.py +219 -0
  23. topup_client/api/api_keys/revoke_api_key.py +186 -0
  24. topup_client/api/api_keys/roll_api_key.py +232 -0
  25. topup_client/api/attestation/__init__.py +1 -0
  26. topup_client/api/attestation/get_attestation.py +196 -0
  27. topup_client/api/balance/__init__.py +1 -0
  28. topup_client/api/balance/get_balance.py +150 -0
  29. topup_client/api/config/__init__.py +1 -0
  30. topup_client/api/config/get_config.py +146 -0
  31. topup_client/api/deposit_addresses/__init__.py +1 -0
  32. topup_client/api/deposit_addresses/create_deposit_address.py +220 -0
  33. topup_client/api/deposit_addresses/get_deposit_address.py +220 -0
  34. topup_client/api/deposit_addresses/list_deposit_addresses.py +244 -0
  35. topup_client/api/deposit_addresses/rotate_deposit_address.py +206 -0
  36. topup_client/api/deposit_addresses/update_deposit_address.py +240 -0
  37. topup_client/api/deposits/__init__.py +1 -0
  38. topup_client/api/deposits/get_deposit.py +199 -0
  39. topup_client/api/deposits/list_deposits.py +368 -0
  40. topup_client/api/deposits/update_deposit.py +240 -0
  41. topup_client/api/events/__init__.py +1 -0
  42. topup_client/api/events/get_event.py +173 -0
  43. topup_client/api/events/list_events.py +327 -0
  44. topup_client/api/events/resend_event.py +232 -0
  45. topup_client/api/forwarders/__init__.py +1 -0
  46. topup_client/api/forwarders/list_forwarders.py +291 -0
  47. topup_client/api/quotes/__init__.py +1 -0
  48. topup_client/api/quotes/cancel_quote.py +206 -0
  49. topup_client/api/quotes/create_quote.py +217 -0
  50. topup_client/api/quotes/get_quote.py +235 -0
  51. topup_client/api/quotes/list_quotes.py +240 -0
  52. topup_client/api/quotes/update_quote.py +236 -0
  53. topup_client/api/refunds/__init__.py +1 -0
  54. topup_client/api/refunds/cancel_refund.py +222 -0
  55. topup_client/api/refunds/create_refund.py +233 -0
  56. topup_client/api/refunds/get_refund.py +199 -0
  57. topup_client/api/refunds/list_refunds.py +240 -0
  58. topup_client/api/refunds/mark_refund_paid.py +252 -0
  59. topup_client/api/refunds/update_refund.py +236 -0
  60. topup_client/api/sweeps/__init__.py +1 -0
  61. topup_client/api/sweeps/list_sweeps.py +263 -0
  62. topup_client/api/treasuries/__init__.py +1 -0
  63. topup_client/api/treasuries/cancel_treasury.py +206 -0
  64. topup_client/api/treasuries/create_treasury.py +278 -0
  65. topup_client/api/treasuries/create_treasury_challenge.py +221 -0
  66. topup_client/api/treasuries/get_treasury.py +173 -0
  67. topup_client/api/treasuries/list_treasuries.py +244 -0
  68. topup_client/api/treasuries/pause_treasury.py +214 -0
  69. topup_client/api/treasuries/resume_treasury.py +210 -0
  70. topup_client/api/webhook_endpoints/__init__.py +1 -0
  71. topup_client/api/webhook_endpoints/create_webhook_endpoint.py +224 -0
  72. topup_client/api/webhook_endpoints/delete_webhook_endpoint.py +177 -0
  73. topup_client/api/webhook_endpoints/get_webhook_endpoint.py +173 -0
  74. topup_client/api/webhook_endpoints/list_webhook_endpoints.py +210 -0
  75. topup_client/api/webhook_endpoints/test_webhook_endpoint.py +205 -0
  76. topup_client/api/webhook_endpoints/update_webhook_endpoint.py +240 -0
  77. topup_client/client.py +272 -0
  78. topup_client/errors.py +16 -0
  79. topup_client/models/__init__.py +203 -0
  80. topup_client/models/account_object.py +174 -0
  81. topup_client/models/account_object_object.py +13 -0
  82. topup_client/models/account_self_pause_request.py +53 -0
  83. topup_client/models/api_key_list.py +115 -0
  84. topup_client/models/api_key_list_object.py +13 -0
  85. topup_client/models/api_key_object.py +227 -0
  86. topup_client/models/api_key_object_object.py +13 -0
  87. topup_client/models/attestation_response.py +136 -0
  88. topup_client/models/attestation_response_object.py +15 -0
  89. topup_client/models/balance.py +106 -0
  90. topup_client/models/balance_amount.py +114 -0
  91. topup_client/models/balance_object.py +13 -0
  92. topup_client/models/client_deposit_address.py +160 -0
  93. topup_client/models/client_deposit_address_network.py +98 -0
  94. topup_client/models/client_deposit_address_object.py +15 -0
  95. topup_client/models/client_deposit_address_payment.py +164 -0
  96. topup_client/models/client_quote.py +209 -0
  97. topup_client/models/client_quote_object.py +13 -0
  98. topup_client/models/config.py +143 -0
  99. topup_client/models/config_asset.py +174 -0
  100. topup_client/models/config_object.py +13 -0
  101. topup_client/models/confirmation_policy.py +74 -0
  102. topup_client/models/create_api_key_request.py +111 -0
  103. topup_client/models/create_deposit_address_request.py +97 -0
  104. topup_client/models/create_quote_request.py +130 -0
  105. topup_client/models/create_refund_request.py +127 -0
  106. topup_client/models/create_treasury_challenge_request.py +58 -0
  107. topup_client/models/create_treasury_request.py +73 -0
  108. topup_client/models/create_webhook_endpoint_request.py +126 -0
  109. topup_client/models/deleted_webhook_endpoint.py +88 -0
  110. topup_client/models/deleted_webhook_endpoint_object.py +15 -0
  111. topup_client/models/delivery_attempt.py +90 -0
  112. topup_client/models/deposit.py +518 -0
  113. topup_client/models/deposit_address.py +270 -0
  114. topup_client/models/deposit_address_asset.py +90 -0
  115. topup_client/models/deposit_address_list.py +119 -0
  116. topup_client/models/deposit_address_list_object.py +15 -0
  117. topup_client/models/deposit_address_metadata.py +53 -0
  118. topup_client/models/deposit_address_network.py +108 -0
  119. topup_client/models/deposit_address_object.py +15 -0
  120. topup_client/models/deposit_admin.py +237 -0
  121. topup_client/models/deposit_event_delivery.py +117 -0
  122. topup_client/models/deposit_list.py +120 -0
  123. topup_client/models/deposit_list_object.py +13 -0
  124. topup_client/models/deposit_metadata.py +54 -0
  125. topup_client/models/deposit_object.py +13 -0
  126. topup_client/models/deposit_transition.py +109 -0
  127. topup_client/models/error_detail.py +117 -0
  128. topup_client/models/error_response.py +80 -0
  129. topup_client/models/error_type.py +15 -0
  130. topup_client/models/event_data.py +113 -0
  131. topup_client/models/event_data_object.py +53 -0
  132. topup_client/models/event_data_previous_attributes_type_0.py +53 -0
  133. topup_client/models/event_list.py +123 -0
  134. topup_client/models/event_list_object.py +13 -0
  135. topup_client/models/event_object_response.py +190 -0
  136. topup_client/models/event_object_response_object.py +15 -0
  137. topup_client/models/event_request.py +88 -0
  138. topup_client/models/forwarder.py +190 -0
  139. topup_client/models/forwarder_list.py +115 -0
  140. topup_client/models/forwarder_list_object.py +13 -0
  141. topup_client/models/forwarder_object.py +13 -0
  142. topup_client/models/mark_refund_paid_request.py +77 -0
  143. topup_client/models/metadata_clear.py +13 -0
  144. topup_client/models/metadata_param_type_0.py +50 -0
  145. topup_client/models/payment.py +187 -0
  146. topup_client/models/quote.py +314 -0
  147. topup_client/models/quote_list.py +121 -0
  148. topup_client/models/quote_list_object.py +13 -0
  149. topup_client/models/quote_metadata.py +50 -0
  150. topup_client/models/quote_object.py +13 -0
  151. topup_client/models/refund.py +249 -0
  152. topup_client/models/refund_list.py +116 -0
  153. topup_client/models/refund_list_object.py +13 -0
  154. topup_client/models/refund_metadata.py +50 -0
  155. topup_client/models/refund_object.py +13 -0
  156. topup_client/models/resend_event_request.py +50 -0
  157. topup_client/models/roll_api_key_request.py +52 -0
  158. topup_client/models/roll_webhook_key_request.py +53 -0
  159. topup_client/models/sweep.py +188 -0
  160. topup_client/models/sweep_list.py +117 -0
  161. topup_client/models/sweep_list_object.py +13 -0
  162. topup_client/models/sweep_object.py +13 -0
  163. topup_client/models/treasury.py +227 -0
  164. topup_client/models/treasury_challenge.py +128 -0
  165. topup_client/models/treasury_challenge_object.py +15 -0
  166. topup_client/models/treasury_list.py +115 -0
  167. topup_client/models/treasury_list_object.py +13 -0
  168. topup_client/models/treasury_object.py +13 -0
  169. topup_client/models/update_account_object_request.py +92 -0
  170. topup_client/models/update_metadata_request.py +88 -0
  171. topup_client/models/update_webhook_endpoint_request.py +179 -0
  172. topup_client/models/webhook_endpoint_list.py +116 -0
  173. topup_client/models/webhook_endpoint_list_object.py +15 -0
  174. topup_client/models/webhook_endpoint_object.py +274 -0
  175. topup_client/models/webhook_endpoint_object_metadata.py +50 -0
  176. topup_client/models/webhook_endpoint_object_object.py +15 -0
  177. topup_client/models/webhook_key_object.py +97 -0
  178. topup_client/models/webhook_key_version.py +89 -0
  179. topup_client/types.py +54 -0
  180. topup_sdk/__init__.py +79 -0
  181. topup_sdk/__main__.py +171 -0
  182. topup_sdk/addresses.py +168 -0
  183. topup_sdk/attestation.py +73 -0
  184. topup_sdk/client.py +1188 -0
  185. topup_sdk/errors.py +57 -0
  186. topup_sdk/export.py +51 -0
  187. topup_sdk/fulfillment.py +143 -0
  188. topup_sdk/ids.py +28 -0
  189. topup_sdk/py.typed +0 -0
  190. topup_sdk/signing.py +329 -0
  191. topup_sdk/sweeps.py +210 -0
  192. topup_sdk/treasury.py +47 -0
  193. topup_sdk/webhooks.py +162 -0
phala_pay/__init__.py ADDED
@@ -0,0 +1,96 @@
1
+ """Phala Pay for Python, in the shape of Stripe's.
2
+
3
+ import os
4
+
5
+ from phala_pay import PhalaPay
6
+
7
+ pay = PhalaPay(
8
+ api_base="https://pay.example.com",
9
+ api_key=os.environ["PHALA_PAY_KEY"],
10
+ forwarder=(FACTORY, IMPLEMENTATION),
11
+ )
12
+ quote = pay.quotes.create(
13
+ client_reference_id="team-42", amount=2500, chain_id=11155111, asset="pha"
14
+ )
15
+
16
+ event = pay.webhooks.construct_event(
17
+ raw_body, request.headers, WEBHOOK_PUBLIC_KEY, "acct_…", expected_livemode=False
18
+ )
19
+ if event.type == "deposit.credited":
20
+ credit_once(event.deposit.id, event.deposit.client_reference_id, event.deposit.amount)
21
+
22
+ `topup_sdk` holds the lower-level pieces (address derivation, attestation, webhook and admin
23
+ request signatures, offline `flush_transaction` and `safe_batch`, `export_account`) and
24
+ `topup_client` the client generated from the OpenAPI document.
25
+ """
26
+
27
+ from topup_client.models import (
28
+ Balance,
29
+ ClientQuote,
30
+ Config,
31
+ Deposit,
32
+ DepositAddress,
33
+ Forwarder,
34
+ Quote,
35
+ Refund,
36
+ Sweep,
37
+ Treasury,
38
+ )
39
+ from topup_sdk import (
40
+ AddressMismatchError,
41
+ ApiError,
42
+ TopupError,
43
+ flush_transaction,
44
+ flush_transactions,
45
+ safe_batch,
46
+ write_safe_batch,
47
+ )
48
+
49
+ from ._client import PhalaPay
50
+ from ._types import (
51
+ ApiKeyStatus,
52
+ DepositAddressStatus,
53
+ DepositStatus,
54
+ EventType,
55
+ PaymentStatus,
56
+ QuoteStatus,
57
+ RefundStatus,
58
+ TreasuryStatus,
59
+ WebhookEndpointStatus,
60
+ )
61
+ from ._webhook import Event, EventData, EventRequest, SignatureVerificationError, Webhook
62
+
63
+ __all__ = [
64
+ "AddressMismatchError",
65
+ "ApiError",
66
+ "ApiKeyStatus",
67
+ "Balance",
68
+ "ClientQuote",
69
+ "Config",
70
+ "Deposit",
71
+ "DepositAddress",
72
+ "DepositAddressStatus",
73
+ "DepositStatus",
74
+ "Event",
75
+ "EventData",
76
+ "EventRequest",
77
+ "EventType",
78
+ "Forwarder",
79
+ "PaymentStatus",
80
+ "PhalaPay",
81
+ "Quote",
82
+ "QuoteStatus",
83
+ "Refund",
84
+ "RefundStatus",
85
+ "SignatureVerificationError",
86
+ "Sweep",
87
+ "TopupError",
88
+ "Treasury",
89
+ "TreasuryStatus",
90
+ "Webhook",
91
+ "WebhookEndpointStatus",
92
+ "flush_transaction",
93
+ "flush_transactions",
94
+ "safe_batch",
95
+ "write_safe_batch",
96
+ ]
phala_pay/_client.py ADDED
@@ -0,0 +1,582 @@
1
+ """`PhalaPay`: the Phala Pay merchant API as Stripe-style resources over `TopupClient`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator, Mapping, Sequence
6
+ from pathlib import Path
7
+
8
+ import httpx
9
+
10
+ from topup_client.models import (
11
+ AccountObject,
12
+ ApiKeyObject,
13
+ Balance,
14
+ Config,
15
+ DeletedWebhookEndpoint,
16
+ Deposit,
17
+ DepositAddress,
18
+ EventObjectResponse,
19
+ Forwarder,
20
+ Quote,
21
+ Refund,
22
+ Sweep,
23
+ Treasury,
24
+ TreasuryChallenge,
25
+ WebhookEndpointObject,
26
+ )
27
+ from topup_sdk import TopupClient, export_account, sign_treasury_challenge
28
+ from topup_sdk.client import Metadata
29
+
30
+ from ._types import (
31
+ DepositAddressStatus,
32
+ DepositStatus,
33
+ EventType,
34
+ QuoteStatus,
35
+ RefundStatus,
36
+ TreasuryStatus,
37
+ )
38
+ from ._webhook import Webhook
39
+
40
+
41
+ class PhalaPay:
42
+ """A client for one account and mode, authenticated with its API key.
43
+
44
+ pay = PhalaPay(
45
+ api_base="https://pay.example.com",
46
+ api_key=os.environ["PHALA_PAY_KEY"],
47
+ account="acct_…",
48
+ forwarder=(FACTORY, IMPLEMENTATION),
49
+ treasuries={1: "0x…your treasury on Ethereum"},
50
+ )
51
+ quote = pay.quotes.create(client_reference_id="team-42", amount=2500,
52
+ chain_id=11155111, asset="pha")
53
+ return {"client_secret": quote.client_secret, "expected_address": quote.address}
54
+
55
+ `api_key` is a restricted key (`ppay_rk_…`, recommended for a production server) or a secret
56
+ key (`ppay_sk_…`, for administration); the key selects the account and the mode. Every quote
57
+ and deposit address is recomputed before it is returned from pins you configure yourself:
58
+ `forwarder`, the `(factory, implementation)` pair pinned from the attested deployment;
59
+ `treasuries` (`{chain_id: treasury}`), your own treasury per chain; and `account` (`acct_…`).
60
+ The service's `treasury` is never trusted, and an address you cannot derive raises
61
+ `AddressMismatchError` (fail closed). A live key requires all three pins: without them every
62
+ address check raises. In test mode `account` is read from `GET /v1/account` when not given,
63
+ and without `treasuries` the service's treasury is used with an `UnpinnedTreasuryWarning`.
64
+
65
+ Requests that fail with a transport error, `429` (after its `Retry-After`), or `5xx` are
66
+ retried with backoff; `POST`s reuse one `Idempotency-Key` across retries, so a retry never
67
+ creates a second object, and a response the service saved for the key (even a `500`) is
68
+ returned as it was, not retried. A failed request raises `ApiError` with the service's
69
+ `code`, `param`, `doc_url`, and `request_id`.
70
+ """
71
+
72
+ def __init__(
73
+ self,
74
+ api_base: str,
75
+ api_key: str,
76
+ *,
77
+ forwarder: tuple[str, str],
78
+ treasuries: Mapping[int, str] | None = None,
79
+ account: str | None = None,
80
+ timeout: float = 15.0,
81
+ max_attempts: int = 4,
82
+ transport: httpx.BaseTransport | None = None,
83
+ ) -> None:
84
+ self._client = TopupClient(
85
+ api_base,
86
+ api_key,
87
+ account=account,
88
+ forwarder=forwarder,
89
+ treasuries=treasuries,
90
+ timeout=timeout,
91
+ max_attempts=max_attempts,
92
+ transport=transport,
93
+ )
94
+ self.account = AccountResource(self._client)
95
+ self.config = ConfigResource(self._client)
96
+ self.quotes = Quotes(self._client)
97
+ self.deposits = Deposits(self._client)
98
+ self.deposit_addresses = DepositAddresses(self._client)
99
+ self.refunds = Refunds(self._client)
100
+ self.balance = BalanceResource(self._client)
101
+ self.sweeps = Sweeps(self._client)
102
+ self.forwarders = Forwarders(self._client)
103
+ self.treasuries = Treasuries(self._client)
104
+ self.api_keys = ApiKeys(self._client)
105
+ self.webhook_endpoints = WebhookEndpoints(self._client)
106
+ self.events = Events(self._client)
107
+ self.webhooks = Webhook
108
+
109
+ def export_account(self, directory: str | Path) -> dict[str, int]:
110
+ """Writes every object of the key's account and mode to `directory`, one JSON file per
111
+ resource, and returns the counts (design §13). `forwarders.json` keeps funds sweepable
112
+ without Phala Pay."""
113
+ return export_account(self._client, directory)
114
+
115
+ def close(self) -> None:
116
+ self._client.close()
117
+
118
+ def __enter__(self) -> PhalaPay:
119
+ return self
120
+
121
+ def __exit__(self, *_: object) -> None:
122
+ self.close()
123
+
124
+
125
+ class AccountResource:
126
+ def __init__(self, client: TopupClient) -> None:
127
+ self._client = client
128
+
129
+ def retrieve(self) -> AccountObject:
130
+ """The key's account, in the key's mode."""
131
+ return self._client.get_account()
132
+
133
+ def update(self, *, confirmation_policies: Mapping[int, str | None]) -> AccountObject:
134
+ """Requires, per chain, a confirmation stricter than the route's before a payment is
135
+ credited: a depth such as `"12"`, `"safe"`, or `"finalized"`; `None` restores the
136
+ route's."""
137
+ return self._client.update_account(confirmation_policies=confirmation_policies)
138
+
139
+ def pause_quotes(self) -> AccountObject:
140
+ """Stops issuing quotes, deposit addresses, and networks in both modes, for an
141
+ emergency; existing addresses keep being credited."""
142
+ return self._client.pause_quotes()
143
+
144
+ def resume_quotes(self) -> AccountObject:
145
+ """Lifts your own `quotes` pause; an operator's pause stays."""
146
+ return self._client.resume_quotes()
147
+
148
+ def roll_webhook_key(self, *, expires_in: int = 172_800) -> AccountObject:
149
+ """Rolls this mode's webhook signing key; the old one signs beside it for `expires_in`
150
+ seconds: 48 hours (the default) to 7 days live, `0` to 7 days in test mode."""
151
+ return self._client.roll_webhook_key(expires_in=expires_in)
152
+
153
+
154
+ class ConfigResource:
155
+ def __init__(self, client: TopupClient) -> None:
156
+ self._client = client
157
+
158
+ def retrieve(self) -> Config:
159
+ """The payable assets, limits, quote terms, and confirmations, for the product's UI."""
160
+ return self._client.get_config()
161
+
162
+
163
+ class Quotes:
164
+ def __init__(self, client: TopupClient) -> None:
165
+ self._client = client
166
+
167
+ def create(
168
+ self,
169
+ *,
170
+ client_reference_id: str,
171
+ amount: int,
172
+ chain_id: int,
173
+ asset: str,
174
+ currency: str = "usd",
175
+ idempotency_key: str | None = None,
176
+ metadata: Mapping[str, str] | None = None,
177
+ ) -> Quote:
178
+ """Quotes `amount` cents for the customer `client_reference_id`, payable in `asset` on
179
+ `chain_id`.
180
+
181
+ Only this response carries `client_secret`, the value the payer's browser needs, beside
182
+ the recomputed `address` to pass as `<Checkout expectedAddress>`. Pass an
183
+ `idempotency_key` of your own (for example your order id) to resume a checkout: for 24
184
+ hours a repeat of the same request replays the first response, the same quote and the
185
+ same `client_secret` included.
186
+
187
+ `metadata` is Stripe's: up to 50 string pairs for your own use, such as your order id,
188
+ copied to the deposit that pays the quote. Do not store sensitive information in it.
189
+ """
190
+ return self._client.create_quote(
191
+ client_reference_id,
192
+ amount,
193
+ chain_id=chain_id,
194
+ asset=asset,
195
+ currency=currency,
196
+ idempotency_key=idempotency_key,
197
+ metadata=metadata,
198
+ )
199
+
200
+ def retrieve(self, quote_id: str) -> Quote:
201
+ return self._client.get_quote(quote_id)
202
+
203
+ def list(
204
+ self, *, client_reference_id: str | None = None, status: QuoteStatus | None = None
205
+ ) -> Iterator[Quote]:
206
+ """Yields every matching quote, newest first, fetching pages as it goes."""
207
+ return self._client.list_quotes(client_reference_id=client_reference_id, status=status)
208
+
209
+ def update(self, quote_id: str, *, metadata: Metadata | None = None) -> Quote:
210
+ """Merges `metadata` into the quote's: a key set to `""` is unset, and `metadata=""`
211
+ unsets every key."""
212
+ return self._client.update_quote(quote_id, metadata=metadata)
213
+
214
+ def cancel(self, quote_id: str) -> Quote:
215
+ """Cancels an open quote no payment has reached; repeating it returns the canceled quote."""
216
+ return self._client.cancel_quote(quote_id)
217
+
218
+
219
+ class Deposits:
220
+ def __init__(self, client: TopupClient) -> None:
221
+ self._client = client
222
+
223
+ # Before `list`, whose name would shadow the builtin in later annotations.
224
+ def retrieve(self, deposit_id: str, *, expand: list[str] | None = None) -> Deposit:
225
+ return self._client.get_deposit(deposit_id, expand=expand)
226
+
227
+ def update(self, deposit_id: str, *, metadata: Metadata | None = None) -> Deposit:
228
+ """Merges `metadata` into the deposit's, which started as a copy of its quote's or its
229
+ deposit address's; theirs are left unchanged."""
230
+ return self._client.update_deposit(deposit_id, metadata=metadata)
231
+
232
+ def list(
233
+ self,
234
+ *,
235
+ client_reference_id: str | None = None,
236
+ quote: str | None = None,
237
+ deposit_address: str | None = None,
238
+ status: DepositStatus | None = None,
239
+ tx_hash: str | None = None,
240
+ created_gt: int | None = None,
241
+ created_gte: int | None = None,
242
+ created_lt: int | None = None,
243
+ created_lte: int | None = None,
244
+ expand: list[str] | None = None,
245
+ ) -> Iterator[Deposit]:
246
+ """Yields every matching deposit, newest first, fetching pages as it goes. `created_*`
247
+ are Unix seconds (Stripe's `created[gt|gte|lt|lte]`)."""
248
+ return self._client.list_deposits(
249
+ client_reference_id=client_reference_id,
250
+ quote=quote,
251
+ deposit_address=deposit_address,
252
+ status=status,
253
+ tx_hash=tx_hash,
254
+ created_gt=created_gt,
255
+ created_gte=created_gte,
256
+ created_lt=created_lt,
257
+ created_lte=created_lte,
258
+ expand=expand,
259
+ )
260
+
261
+
262
+ class DepositAddresses:
263
+ """A customer's persistent deposit address: one address for every supported token on every
264
+ supported network. Show it like a bank account number; any amount of a supported token sent
265
+ to it is credited at the market rate when it arrives. `networks` lists each chain's address
266
+ (the same wherever the treasury is the same; `address` is it when all agree) and tokens, and
267
+ `payments` the transfers seen and recorded in the last 24 hours.
268
+
269
+ `topup_sdk.deposit_address(...)` recomputes any version offline from its salt inputs and a
270
+ network's treasury.
271
+ """
272
+
273
+ def __init__(self, client: TopupClient) -> None:
274
+ self._client = client
275
+
276
+ def create(
277
+ self,
278
+ *,
279
+ client_reference_id: str,
280
+ metadata: Metadata | None = None,
281
+ ) -> DepositAddress:
282
+ """Returns the customer's active address, recomputed; the same call keeps returning it
283
+ until it is rotated, and adds a network supported since. Each response carries a new
284
+ `client_secret` for the customer's page (`<DepositAddress clientSecret>`) to follow
285
+ payments. `metadata` is merged into the address's; each deposit to it starts with a
286
+ copy, and a rotation carries it to the next address."""
287
+ return self._client.create_deposit_address(client_reference_id, metadata=metadata)
288
+
289
+ def update(
290
+ self, deposit_address_id: str, *, metadata: Metadata | None = None
291
+ ) -> DepositAddress:
292
+ """Merges `metadata` into the address's (a key set to `""` is unset, `""` unsets all)."""
293
+ return self._client.update_deposit_address(deposit_address_id, metadata=metadata)
294
+
295
+ def retrieve(self, deposit_address_id: str) -> DepositAddress:
296
+ return self._client.get_deposit_address(deposit_address_id)
297
+
298
+ def list(
299
+ self,
300
+ *,
301
+ client_reference_id: str | None = None,
302
+ status: DepositAddressStatus | None = None,
303
+ ) -> Iterator[DepositAddress]:
304
+ """Yields every matching deposit address, newest first, fetching pages as it goes."""
305
+ return self._client.list_deposit_addresses(
306
+ client_reference_id=client_reference_id, status=status
307
+ )
308
+
309
+ def rotate(
310
+ self, deposit_address_id: str, *, idempotency_key: str | None = None
311
+ ) -> DepositAddress:
312
+ """Retires the address and returns the customer's new one, a new address on every
313
+ network; the retired address is still credited, so stop showing it rather than telling
314
+ the customer it is invalid."""
315
+ return self._client.rotate_deposit_address(
316
+ deposit_address_id, idempotency_key=idempotency_key
317
+ )
318
+
319
+
320
+ class Refunds:
321
+ def __init__(self, client: TopupClient) -> None:
322
+ self._client = client
323
+
324
+ def create(
325
+ self,
326
+ *,
327
+ deposit: str,
328
+ destination_address: str,
329
+ amount_atomic: int | None = None,
330
+ idempotency_key: str | None = None,
331
+ metadata: Mapping[str, str] | None = None,
332
+ ) -> Refund:
333
+ """Creates a pending refund of a final `deposit` (its unrefunded remainder unless
334
+ `amount_atomic` is given) to an address the customer controls; pay it from its
335
+ `treasury`, then call `mark_paid`."""
336
+ return self._client.create_refund(
337
+ deposit,
338
+ destination_address,
339
+ amount_atomic,
340
+ idempotency_key=idempotency_key,
341
+ metadata=metadata,
342
+ )
343
+
344
+ def mark_paid(
345
+ self, refund_id: str, *, transaction_hash: str, receipt_log_index: int | None = None
346
+ ) -> Refund:
347
+ """Attaches the transaction that pays the refund; it is verified at finality. From then
348
+ on the refund cannot be canceled: it stays pending until it succeeds, or fails when the
349
+ transaction does not pay it or is proven dropped."""
350
+ return self._client.mark_refund_paid(
351
+ refund_id, transaction_hash, receipt_log_index=receipt_log_index
352
+ )
353
+
354
+ def cancel(self, refund_id: str) -> Refund:
355
+ """Cancels a pending refund."""
356
+ return self._client.cancel_refund(refund_id)
357
+
358
+ def retrieve(self, refund_id: str, *, expand: list[str] | None = None) -> Refund:
359
+ return self._client.get_refund(refund_id, expand=expand)
360
+
361
+ def update(self, refund_id: str, *, metadata: Metadata | None = None) -> Refund:
362
+ """Merges `metadata` into the refund's."""
363
+ return self._client.update_refund(refund_id, metadata=metadata)
364
+
365
+ def list(
366
+ self, *, deposit: str | None = None, status: RefundStatus | None = None
367
+ ) -> Iterator[Refund]:
368
+ """Yields every matching refund, newest first, fetching pages as it goes."""
369
+ return self._client.list_refunds(deposit=deposit, status=status)
370
+
371
+
372
+ class BalanceResource:
373
+ def __init__(self, client: TopupClient) -> None:
374
+ self._client = client
375
+
376
+ def retrieve(self) -> Balance:
377
+ """What the account's forwarders hold, per chain and token, and the final part of it."""
378
+ return self._client.get_balance()
379
+
380
+
381
+ class Sweeps:
382
+ """Sweeps: finalized `Flushed` events that moved a forwarder's balance to its treasury.
383
+ Build the `flush` calls offline with `topup_sdk.flush_transactions` from
384
+ `forwarders.list(sweepable=token)`, and for a Safe treasury write them with
385
+ `topup_sdk.safe_batch`."""
386
+
387
+ def __init__(self, client: TopupClient) -> None:
388
+ self._client = client
389
+
390
+ def list(
391
+ self,
392
+ *,
393
+ chain_id: int | None = None,
394
+ forwarder: str | None = None,
395
+ token: str | None = None,
396
+ ) -> Iterator[Sweep]:
397
+ """Yields the sweeps, newest first, fetching pages as it goes."""
398
+ return self._client.list_sweeps(chain_id=chain_id, forwarder=forwarder, token=token)
399
+
400
+
401
+ class Forwarders:
402
+ def __init__(self, client: TopupClient) -> None:
403
+ self._client = client
404
+
405
+ def list(
406
+ self,
407
+ *,
408
+ chain_id: int | None = None,
409
+ quote: str | None = None,
410
+ deposit_address: str | None = None,
411
+ sweepable: str | None = None,
412
+ ) -> Iterator[Forwarder]:
413
+ """Yields the account's forwarders with their `(factory, salt, treasury)`: all of them, or
414
+ those of one `quote` or `deposit_address`; with `sweepable` (a token contract), only those
415
+ safe to sweep of it."""
416
+ return self._client.list_forwarders(
417
+ chain_id=chain_id, quote=quote, deposit_address=deposit_address, sweepable=sweepable
418
+ )
419
+
420
+
421
+ class Treasuries:
422
+ """Treasuries, proven through the API (design D10): an EOA signs the challenge with
423
+ `set_eoa`; a Safe's owners sign it as a Safe message and `create` submits it
424
+ (docs/integration.md, "Set a Safe as treasury")."""
425
+
426
+ def __init__(self, client: TopupClient) -> None:
427
+ self._client = client
428
+
429
+ def challenge(self, *, chain_id: int, address: str) -> TreasuryChallenge:
430
+ """The EIP-4361 message that proves `address` as the treasury of `chain_id`."""
431
+ return self._client.create_treasury_challenge(chain_id, address)
432
+
433
+ def create(self, *, chain_id: int, message: str, signature: str) -> Treasury:
434
+ """Submits a signed challenge `message`."""
435
+ return self._client.create_treasury(chain_id, message, signature)
436
+
437
+ def set_eoa(self, *, chain_id: int, address: str, private_key: str | bytes) -> Treasury:
438
+ """Proves the EOA `address` as the treasury of `chain_id`: requests a challenge, signs it
439
+ with `private_key` (which must be `address`'s; needs `phala-pay[eoa]`), and submits it."""
440
+ challenge = self.challenge(chain_id=chain_id, address=address)
441
+ signature = sign_treasury_challenge(challenge.message, private_key, address=address)
442
+ return self.create(chain_id=chain_id, message=challenge.message, signature=signature)
443
+
444
+ def retrieve(self, treasury_id: str) -> Treasury:
445
+ return self._client.get_treasury(treasury_id)
446
+
447
+ def list(
448
+ self, *, chain_id: int | None = None, status: TreasuryStatus | None = None
449
+ ) -> list[Treasury]:
450
+ """The treasuries of this mode, newest first."""
451
+ return self._client.list_treasuries(chain_id=chain_id, status=status)
452
+
453
+ def cancel(self, treasury_id: str) -> Treasury:
454
+ """Cancels a pending treasury change before it applies."""
455
+ return self._client.cancel_treasury(treasury_id)
456
+
457
+ def pause(self, treasury_id: str) -> Treasury:
458
+ """Pauses crediting of deposits to every forwarder over the treasury, for an incident
459
+ such as a compromised former treasury: they stay `pending` until `resume`."""
460
+ return self._client.pause_treasury(treasury_id)
461
+
462
+ def resume(self, treasury_id: str) -> Treasury:
463
+ """Lifts your crediting pause; an operator's pause stays."""
464
+ return self._client.resume_treasury(treasury_id)
465
+
466
+
467
+ class ApiKeys:
468
+ def __init__(self, client: TopupClient) -> None:
469
+ self._client = client
470
+
471
+ def create(self, *, name: str = "", permissions: Sequence[str] | None = None) -> ApiKeyObject:
472
+ """A new key of this mode; its `secret` is in this response only. With `permissions`
473
+ (such as `["quotes.write", "deposit_addresses.write", "deposits.read", "events.read",
474
+ "refunds.read", "account.read"]`) it is a restricted key, `ppay_rk_…`, which never
475
+ manages keys, treasuries, webhook endpoints, webhook keys, or account settings: run
476
+ production with one and keep the secret key offline."""
477
+ return self._client.create_api_key(name=name, permissions=permissions)
478
+
479
+ def retrieve(self, api_key_id: str) -> ApiKeyObject:
480
+ return self._client.get_api_key(api_key_id)
481
+
482
+ def list(self) -> list[ApiKeyObject]:
483
+ """This mode's keys, without their secrets."""
484
+ return self._client.list_api_keys()
485
+
486
+ def roll(self, api_key_id: str, *, expires_in: int = 0) -> ApiKeyObject:
487
+ """A replacement key of the same kind and permissions with its `secret`; the old one
488
+ works for `expires_in` seconds."""
489
+ return self._client.roll_api_key(api_key_id, expires_in=expires_in)
490
+
491
+ def revoke(self, api_key_id: str) -> ApiKeyObject:
492
+ return self._client.revoke_api_key(api_key_id)
493
+
494
+
495
+ class WebhookEndpoints:
496
+ def __init__(self, client: TopupClient) -> None:
497
+ self._client = client
498
+
499
+ def create(
500
+ self,
501
+ *,
502
+ url: str,
503
+ enabled_events: list[str],
504
+ description: str | None = None,
505
+ metadata: Mapping[str, str] | None = None,
506
+ ) -> WebhookEndpointObject:
507
+ return self._client.create_webhook_endpoint(
508
+ url, enabled_events, description=description, metadata=metadata
509
+ )
510
+
511
+ def retrieve(self, endpoint_id: str) -> WebhookEndpointObject:
512
+ return self._client.get_webhook_endpoint(endpoint_id)
513
+
514
+ def update(
515
+ self,
516
+ endpoint_id: str,
517
+ *,
518
+ url: str | None = None,
519
+ enabled_events: list[str] | None = None,
520
+ description: str | None = None,
521
+ disabled: bool | None = None,
522
+ metadata: Metadata | None = None,
523
+ ) -> WebhookEndpointObject:
524
+ return self._client.update_webhook_endpoint(
525
+ endpoint_id,
526
+ url=url,
527
+ enabled_events=enabled_events,
528
+ description=description,
529
+ disabled=disabled,
530
+ metadata=metadata,
531
+ )
532
+
533
+ def delete(self, endpoint_id: str) -> DeletedWebhookEndpoint:
534
+ return self._client.delete_webhook_endpoint(endpoint_id)
535
+
536
+ def test(self, endpoint_id: str) -> EventObjectResponse:
537
+ """Sends a test event to the endpoint."""
538
+ return self._client.test_webhook_endpoint(endpoint_id)
539
+
540
+ def list(self) -> Iterator[WebhookEndpointObject]:
541
+ return self._client.list_webhook_endpoints()
542
+
543
+
544
+ class Events:
545
+ """Events of this mode: every webhook ever sent, and the account's audit log."""
546
+
547
+ def __init__(self, client: TopupClient) -> None:
548
+ self._client = client
549
+
550
+ def retrieve(self, event_id: str) -> EventObjectResponse:
551
+ return self._client.get_event(event_id)
552
+
553
+ def resend(self, event_id: str, *, webhook_endpoint: str) -> EventObjectResponse:
554
+ """Delivers the event again to one enabled endpoint."""
555
+ return self._client.resend_event(event_id, webhook_endpoint=webhook_endpoint)
556
+
557
+ def list(
558
+ self,
559
+ *,
560
+ type: EventType | str | None = None,
561
+ types: Sequence[EventType | str] | None = None,
562
+ delivery_success: bool | None = None,
563
+ created_gt: int | None = None,
564
+ created_gte: int | None = None,
565
+ created_lt: int | None = None,
566
+ created_lte: int | None = None,
567
+ ) -> Iterator[EventObjectResponse]:
568
+ """Yields events, newest first. `type` filters by one type, such as `deposit.reversed`,
569
+ or a group, `deposit.*`; `types` by up to 20. `delivery_success=False` yields the events
570
+ a webhook endpoint has not received: its `pending_deliveries`, `oldest_pending_at`, and
571
+ `last_attempt` say whether it is keeping up; resend the missed events once it is fixed.
572
+ `data.object` is the object as it was when the event happened; `*.updated` events carry
573
+ `data.previous_attributes`, and events caused by your requests their `request`."""
574
+ return self._client.list_events(
575
+ type=type,
576
+ types=types,
577
+ delivery_success=delivery_success,
578
+ created_gt=created_gt,
579
+ created_gte=created_gte,
580
+ created_lt=created_lt,
581
+ created_lte=created_lte,
582
+ )