svc-infra 0.1.586__py3-none-any.whl → 0.1.587__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.
Potentially problematic release.
This version of svc-infra might be problematic. Click here for more details.
- svc_infra/apf_payments/provider/stripe.py +670 -100
- {svc_infra-0.1.586.dist-info → svc_infra-0.1.587.dist-info}/METADATA +1 -1
- {svc_infra-0.1.586.dist-info → svc_infra-0.1.587.dist-info}/RECORD +5 -5
- {svc_infra-0.1.586.dist-info → svc_infra-0.1.587.dist-info}/WHEEL +0 -0
- {svc_infra-0.1.586.dist-info → svc_infra-0.1.587.dist-info}/entry_points.txt +0 -0
|
@@ -6,15 +6,37 @@ from typing import Any, Optional
|
|
|
6
6
|
import anyio
|
|
7
7
|
|
|
8
8
|
from ..schemas import (
|
|
9
|
+
BalanceSnapshotOut,
|
|
9
10
|
CustomerOut,
|
|
10
11
|
CustomerUpsertIn,
|
|
12
|
+
DisputeOut,
|
|
11
13
|
IntentCreateIn,
|
|
12
14
|
IntentOut,
|
|
15
|
+
InvoiceCreateIn,
|
|
13
16
|
InvoiceLineItemIn,
|
|
17
|
+
InvoiceLineItemOut,
|
|
14
18
|
InvoiceOut,
|
|
15
19
|
NextAction,
|
|
20
|
+
PaymentMethodAttachIn,
|
|
21
|
+
PaymentMethodOut,
|
|
22
|
+
PaymentMethodUpdateIn,
|
|
23
|
+
PayoutOut,
|
|
24
|
+
PriceCreateIn,
|
|
25
|
+
PriceOut,
|
|
26
|
+
PriceUpdateIn,
|
|
27
|
+
ProductCreateIn,
|
|
28
|
+
ProductOut,
|
|
29
|
+
ProductUpdateIn,
|
|
16
30
|
RefundIn,
|
|
31
|
+
RefundOut,
|
|
32
|
+
SetupIntentCreateIn,
|
|
33
|
+
SetupIntentOut,
|
|
34
|
+
SubscriptionCreateIn,
|
|
35
|
+
SubscriptionOut,
|
|
36
|
+
SubscriptionUpdateIn,
|
|
17
37
|
UsageRecordIn,
|
|
38
|
+
UsageRecordListFilter,
|
|
39
|
+
UsageRecordOut,
|
|
18
40
|
)
|
|
19
41
|
from ..settings import get_payments_settings
|
|
20
42
|
from .base import ProviderAdapter
|
|
@@ -56,6 +78,110 @@ def _inv_to_out(inv) -> InvoiceOut:
|
|
|
56
78
|
)
|
|
57
79
|
|
|
58
80
|
|
|
81
|
+
def _pm_to_out(pm, *, is_default: bool = False) -> PaymentMethodOut:
|
|
82
|
+
card = getattr(pm, "card", None) or {}
|
|
83
|
+
return PaymentMethodOut(
|
|
84
|
+
id=pm.id,
|
|
85
|
+
provider="stripe",
|
|
86
|
+
provider_customer_id=getattr(pm, "customer", None) or "",
|
|
87
|
+
provider_method_id=pm.id,
|
|
88
|
+
brand=card.get("brand"),
|
|
89
|
+
last4=card.get("last4"),
|
|
90
|
+
exp_month=card.get("exp_month"),
|
|
91
|
+
exp_year=card.get("exp_year"),
|
|
92
|
+
is_default=bool(is_default),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _product_to_out(p) -> ProductOut:
|
|
97
|
+
return ProductOut(
|
|
98
|
+
id=p.id,
|
|
99
|
+
provider="stripe",
|
|
100
|
+
provider_product_id=p.id,
|
|
101
|
+
name=p.name,
|
|
102
|
+
active=bool(p.active),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _price_to_out(pr) -> PriceOut:
|
|
107
|
+
rec = getattr(pr, "recurring", None) or {}
|
|
108
|
+
return PriceOut(
|
|
109
|
+
id=pr.id,
|
|
110
|
+
provider="stripe",
|
|
111
|
+
provider_price_id=pr.id,
|
|
112
|
+
provider_product_id=(
|
|
113
|
+
pr.product if isinstance(pr.product, str) else getattr(pr.product, "id", "")
|
|
114
|
+
),
|
|
115
|
+
currency=str(pr.currency).upper(),
|
|
116
|
+
unit_amount=int(pr.unit_amount),
|
|
117
|
+
interval=rec.get("interval"),
|
|
118
|
+
trial_days=getattr(pr, "trial_period_days", None),
|
|
119
|
+
active=bool(pr.active),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _sub_to_out(s) -> SubscriptionOut:
|
|
124
|
+
# pick first item’s price/quantity for simple one-item subs
|
|
125
|
+
item = s.items.data[0] if getattr(s.items, "data", []) else None
|
|
126
|
+
price_id = item.price.id if item and getattr(item, "price", None) else ""
|
|
127
|
+
qty = item.quantity if item else 0
|
|
128
|
+
return SubscriptionOut(
|
|
129
|
+
id=s.id,
|
|
130
|
+
provider="stripe",
|
|
131
|
+
provider_subscription_id=s.id,
|
|
132
|
+
provider_price_id=price_id,
|
|
133
|
+
status=s.status,
|
|
134
|
+
quantity=int(qty or 0),
|
|
135
|
+
cancel_at_period_end=bool(s.cancel_at_period_end),
|
|
136
|
+
current_period_end=(
|
|
137
|
+
str(s.current_period_end) if getattr(s, "current_period_end", None) else None
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _refund_to_out(r) -> RefundOut:
|
|
143
|
+
return RefundOut(
|
|
144
|
+
id=r.id,
|
|
145
|
+
provider="stripe",
|
|
146
|
+
provider_refund_id=r.id,
|
|
147
|
+
provider_payment_intent_id=getattr(r, "payment_intent", None),
|
|
148
|
+
amount=int(r.amount),
|
|
149
|
+
currency=str(r.currency).upper(),
|
|
150
|
+
status=r.status,
|
|
151
|
+
reason=getattr(r, "reason", None),
|
|
152
|
+
created_at=str(r.created) if getattr(r, "created", None) else None,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _dispute_to_out(d) -> DisputeOut:
|
|
157
|
+
return DisputeOut(
|
|
158
|
+
id=d.id,
|
|
159
|
+
provider="stripe",
|
|
160
|
+
provider_dispute_id=d.id,
|
|
161
|
+
amount=int(d.amount),
|
|
162
|
+
currency=str(d.currency).upper(),
|
|
163
|
+
reason=getattr(d, "reason", None),
|
|
164
|
+
status=d.status,
|
|
165
|
+
evidence_due_by=(
|
|
166
|
+
str(d.evidence_details.get("due_by")) if getattr(d, "evidence_details", None) else None
|
|
167
|
+
),
|
|
168
|
+
created_at=str(d.created) if getattr(d, "created", None) else None,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _payout_to_out(p) -> PayoutOut:
|
|
173
|
+
return PayoutOut(
|
|
174
|
+
id=p.id,
|
|
175
|
+
provider="stripe",
|
|
176
|
+
provider_payout_id=p.id,
|
|
177
|
+
amount=int(p.amount),
|
|
178
|
+
currency=str(p.currency).upper(),
|
|
179
|
+
status=p.status,
|
|
180
|
+
arrival_date=str(p.arrival_date) if getattr(p, "arrival_date", None) else None,
|
|
181
|
+
type=getattr(p, "type", None),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
59
185
|
class StripeAdapter(ProviderAdapter):
|
|
60
186
|
name = "stripe"
|
|
61
187
|
|
|
@@ -70,8 +196,8 @@ class StripeAdapter(ProviderAdapter):
|
|
|
70
196
|
st.stripe.webhook_secret.get_secret_value() if st.stripe.webhook_secret else None
|
|
71
197
|
)
|
|
72
198
|
|
|
199
|
+
# -------- Customers --------
|
|
73
200
|
async def ensure_customer(self, data: CustomerUpsertIn) -> CustomerOut:
|
|
74
|
-
# try by email (idempotent enough for demo; production can map via your DB)
|
|
75
201
|
if data.email:
|
|
76
202
|
existing = await _acall(stripe.Customer.list, email=data.email, limit=1)
|
|
77
203
|
c = (
|
|
@@ -108,113 +234,265 @@ class StripeAdapter(ProviderAdapter):
|
|
|
108
234
|
name=c.get("name"),
|
|
109
235
|
)
|
|
110
236
|
|
|
111
|
-
async def
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
237
|
+
async def list_customers(
|
|
238
|
+
self, *, provider: str | None, user_id: str | None, limit: int, cursor: str | None
|
|
239
|
+
) -> tuple[list[CustomerOut], str | None]:
|
|
240
|
+
params = {"limit": int(limit)}
|
|
241
|
+
if cursor:
|
|
242
|
+
params["starting_after"] = cursor
|
|
243
|
+
# Stripe has no direct filter for our custom user_id; many teams store mapping in DB.
|
|
244
|
+
# If 'user_id' was stored in metadata, we could search via /v1/customers?limit=... then filter client-side.
|
|
245
|
+
res = await _acall(stripe.Customer.list, **params)
|
|
246
|
+
items = [
|
|
247
|
+
CustomerOut(
|
|
248
|
+
id=c.id,
|
|
249
|
+
provider="stripe",
|
|
250
|
+
provider_customer_id=c.id,
|
|
251
|
+
email=c.get("email"),
|
|
252
|
+
name=c.get("name"),
|
|
253
|
+
)
|
|
254
|
+
for c in res.data
|
|
255
|
+
]
|
|
256
|
+
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
257
|
+
return items, next_cursor
|
|
258
|
+
|
|
259
|
+
# -------- Payment Methods --------
|
|
260
|
+
async def attach_payment_method(self, data: PaymentMethodAttachIn) -> PaymentMethodOut:
|
|
261
|
+
pm = await _acall(
|
|
262
|
+
stripe.PaymentMethod.attach,
|
|
263
|
+
data.payment_method_token,
|
|
264
|
+
customer=data.customer_provider_id,
|
|
118
265
|
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
266
|
+
is_default = False
|
|
267
|
+
if data.make_default:
|
|
268
|
+
cust = await _acall(
|
|
269
|
+
stripe.Customer.modify,
|
|
270
|
+
data.customer_provider_id,
|
|
271
|
+
invoice_settings={"default_payment_method": pm.id},
|
|
272
|
+
)
|
|
273
|
+
is_default = (
|
|
274
|
+
getattr(getattr(cust, "invoice_settings", None), "default_payment_method", None)
|
|
275
|
+
== pm.id
|
|
276
|
+
)
|
|
277
|
+
else:
|
|
278
|
+
cust = await _acall(stripe.Customer.retrieve, data.customer_provider_id)
|
|
279
|
+
is_default = (
|
|
280
|
+
getattr(getattr(cust, "invoice_settings", None), "default_payment_method", None)
|
|
281
|
+
== pm.id
|
|
282
|
+
)
|
|
283
|
+
return _pm_to_out(pm, is_default=is_default)
|
|
284
|
+
|
|
285
|
+
async def list_payment_methods(self, provider_customer_id: str) -> list[PaymentMethodOut]:
|
|
286
|
+
cust = await _acall(stripe.Customer.retrieve, provider_customer_id)
|
|
287
|
+
default_pm = getattr(
|
|
288
|
+
getattr(cust, "invoice_settings", None), "default_payment_method", None
|
|
124
289
|
)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
290
|
+
res = await _acall(stripe.PaymentMethod.list, customer=provider_customer_id, type="card")
|
|
291
|
+
return [_pm_to_out(pm, is_default=(pm.id == default_pm)) for pm in res.data]
|
|
292
|
+
|
|
293
|
+
async def detach_payment_method(self, provider_method_id: str) -> PaymentMethodOut:
|
|
294
|
+
pm = await _acall(stripe.PaymentMethod.detach, provider_method_id)
|
|
295
|
+
# we no longer know default status reliably—fetch customer if set
|
|
296
|
+
cust_id = getattr(pm, "customer", None)
|
|
297
|
+
default_pm = None
|
|
298
|
+
if cust_id:
|
|
299
|
+
cust = await _acall(stripe.Customer.retrieve, cust_id)
|
|
300
|
+
default_pm = getattr(
|
|
301
|
+
getattr(cust, "invoice_settings", None), "default_payment_method", None
|
|
302
|
+
)
|
|
303
|
+
return _pm_to_out(pm, is_default=(pm.id == default_pm))
|
|
304
|
+
|
|
305
|
+
async def set_default_payment_method(
|
|
306
|
+
self, provider_customer_id: str, provider_method_id: str
|
|
307
|
+
) -> PaymentMethodOut:
|
|
308
|
+
cust = await _acall(
|
|
309
|
+
stripe.Customer.modify,
|
|
310
|
+
provider_customer_id,
|
|
311
|
+
invoice_settings={"default_payment_method": provider_method_id},
|
|
312
|
+
)
|
|
313
|
+
pm = await _acall(stripe.PaymentMethod.retrieve, provider_method_id)
|
|
314
|
+
is_default = (
|
|
315
|
+
getattr(getattr(cust, "invoice_settings", None), "default_payment_method", None)
|
|
316
|
+
== pm.id
|
|
134
317
|
)
|
|
318
|
+
return _pm_to_out(pm, is_default=is_default)
|
|
135
319
|
|
|
136
|
-
async def
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
320
|
+
async def get_payment_method(self, provider_method_id: str) -> PaymentMethodOut:
|
|
321
|
+
pm = await _acall(stripe.PaymentMethod.retrieve, provider_method_id)
|
|
322
|
+
cust_id = getattr(pm, "customer", None)
|
|
323
|
+
default_pm = None
|
|
324
|
+
if cust_id:
|
|
325
|
+
cust = await _acall(stripe.Customer.retrieve, cust_id)
|
|
326
|
+
default_pm = getattr(
|
|
327
|
+
getattr(cust, "invoice_settings", None), "default_payment_method", None
|
|
328
|
+
)
|
|
329
|
+
return _pm_to_out(pm, is_default=(pm.id == default_pm))
|
|
330
|
+
|
|
331
|
+
async def update_payment_method(
|
|
332
|
+
self, provider_method_id: str, data: PaymentMethodUpdateIn
|
|
333
|
+
) -> PaymentMethodOut:
|
|
334
|
+
update: dict[str, Any] = {}
|
|
335
|
+
if data.name is not None:
|
|
336
|
+
update["billing_details"] = {"name": data.name}
|
|
337
|
+
if data.exp_month is not None or data.exp_year is not None:
|
|
338
|
+
update["card"] = {}
|
|
339
|
+
if data.exp_month is not None:
|
|
340
|
+
update["card"]["exp_month"] = data.exp_month
|
|
341
|
+
if data.exp_year is not None:
|
|
342
|
+
update["card"]["exp_year"] = data.exp_year
|
|
343
|
+
pm = (
|
|
344
|
+
await _acall(stripe.PaymentMethod.modify, provider_method_id, **update)
|
|
345
|
+
if update
|
|
346
|
+
else await _acall(stripe.PaymentMethod.retrieve, provider_method_id)
|
|
147
347
|
)
|
|
348
|
+
cust_id = getattr(pm, "customer", None)
|
|
349
|
+
default_pm = None
|
|
350
|
+
if cust_id:
|
|
351
|
+
cust = await _acall(stripe.Customer.retrieve, cust_id)
|
|
352
|
+
default_pm = getattr(
|
|
353
|
+
getattr(cust, "invoice_settings", None), "default_payment_method", None
|
|
354
|
+
)
|
|
355
|
+
return _pm_to_out(pm, is_default=(pm.id == default_pm))
|
|
148
356
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
357
|
+
# -------- Products / Prices --------
|
|
358
|
+
async def create_product(self, data: ProductCreateIn) -> ProductOut:
|
|
359
|
+
p = await _acall(stripe.Product.create, name=data.name, active=bool(data.active))
|
|
360
|
+
return _product_to_out(p)
|
|
361
|
+
|
|
362
|
+
async def get_product(self, provider_product_id: str) -> ProductOut:
|
|
363
|
+
p = await _acall(stripe.Product.retrieve, provider_product_id)
|
|
364
|
+
return _product_to_out(p)
|
|
365
|
+
|
|
366
|
+
async def list_products(
|
|
367
|
+
self, *, active: bool | None, limit: int, cursor: str | None
|
|
368
|
+
) -> tuple[list[ProductOut], str | None]:
|
|
369
|
+
params = {"limit": int(limit)}
|
|
370
|
+
if active is not None:
|
|
371
|
+
params["active"] = bool(active)
|
|
372
|
+
if cursor:
|
|
373
|
+
params["starting_after"] = cursor
|
|
374
|
+
res = await _acall(stripe.Product.list, **params)
|
|
375
|
+
items = [_product_to_out(p) for p in res.data]
|
|
376
|
+
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
377
|
+
return items, next_cursor
|
|
378
|
+
|
|
379
|
+
async def update_product(self, provider_product_id: str, data: ProductUpdateIn) -> ProductOut:
|
|
380
|
+
update: dict[str, Any] = {}
|
|
381
|
+
if data.name is not None:
|
|
382
|
+
update["name"] = data.name
|
|
383
|
+
if data.active is not None:
|
|
384
|
+
update["active"] = bool(data.active)
|
|
385
|
+
p = (
|
|
386
|
+
await _acall(stripe.Product.modify, provider_product_id, **update)
|
|
387
|
+
if update
|
|
388
|
+
else await _acall(stripe.Product.retrieve, provider_product_id)
|
|
158
389
|
)
|
|
390
|
+
return _product_to_out(p)
|
|
159
391
|
|
|
160
|
-
async def
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
392
|
+
async def create_price(self, data: PriceCreateIn) -> PriceOut:
|
|
393
|
+
kwargs: dict[str, Any] = dict(
|
|
394
|
+
product=data.provider_product_id,
|
|
395
|
+
currency=data.currency.lower(),
|
|
396
|
+
unit_amount=int(data.unit_amount),
|
|
397
|
+
active=bool(data.active),
|
|
164
398
|
)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
399
|
+
if data.interval:
|
|
400
|
+
kwargs["recurring"] = {"interval": data.interval}
|
|
401
|
+
if data.trial_days is not None:
|
|
402
|
+
kwargs["trial_period_days"] = int(data.trial_days)
|
|
403
|
+
pr = await _acall(stripe.Price.create, **kwargs)
|
|
404
|
+
return _price_to_out(pr)
|
|
405
|
+
|
|
406
|
+
async def get_price(self, provider_price_id: str) -> PriceOut:
|
|
407
|
+
pr = await _acall(stripe.Price.retrieve, provider_price_id)
|
|
408
|
+
return _price_to_out(pr)
|
|
409
|
+
|
|
410
|
+
async def list_prices(
|
|
411
|
+
self,
|
|
412
|
+
*,
|
|
413
|
+
provider_product_id: str | None,
|
|
414
|
+
active: bool | None,
|
|
415
|
+
limit: int,
|
|
416
|
+
cursor: str | None,
|
|
417
|
+
) -> tuple[list[PriceOut], str | None]:
|
|
418
|
+
params = {"limit": int(limit)}
|
|
419
|
+
if provider_product_id:
|
|
420
|
+
params["product"] = provider_product_id
|
|
421
|
+
if active is not None:
|
|
422
|
+
params["active"] = bool(active)
|
|
423
|
+
if cursor:
|
|
424
|
+
params["starting_after"] = cursor
|
|
425
|
+
res = await _acall(stripe.Price.list, **params)
|
|
426
|
+
items = [_price_to_out(p) for p in res.data]
|
|
427
|
+
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
428
|
+
return items, next_cursor
|
|
429
|
+
|
|
430
|
+
async def update_price(self, provider_price_id: str, data: PriceUpdateIn) -> PriceOut:
|
|
431
|
+
# Stripe allows toggling `active` and updating metadata, but not amount/currency/product.
|
|
432
|
+
update: dict[str, Any] = {}
|
|
433
|
+
if data.active is not None:
|
|
434
|
+
update["active"] = bool(data.active)
|
|
435
|
+
pr = (
|
|
436
|
+
await _acall(stripe.Price.modify, provider_price_id, **update)
|
|
437
|
+
if update
|
|
438
|
+
else await _acall(stripe.Price.retrieve, provider_price_id)
|
|
172
439
|
)
|
|
173
|
-
|
|
174
|
-
return await self.hydrate_intent(provider_intent_id)
|
|
440
|
+
return _price_to_out(pr)
|
|
175
441
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
stripe.Webhook.construct_event,
|
|
183
|
-
payload=payload,
|
|
184
|
-
sig_header=signature,
|
|
185
|
-
secret=self._wh_secret,
|
|
442
|
+
# -------- Subscriptions --------
|
|
443
|
+
async def create_subscription(self, data: SubscriptionCreateIn) -> SubscriptionOut:
|
|
444
|
+
kwargs: dict[str, Any] = dict(
|
|
445
|
+
customer=data.customer_provider_id,
|
|
446
|
+
items=[{"price": data.price_provider_id, "quantity": int(data.quantity)}],
|
|
447
|
+
proration_behavior=data.proration_behavior,
|
|
186
448
|
)
|
|
187
|
-
|
|
449
|
+
if data.trial_days is not None:
|
|
450
|
+
kwargs["trial_period_days"] = int(data.trial_days)
|
|
451
|
+
s = await _acall(stripe.Subscription.create, **kwargs)
|
|
452
|
+
return _sub_to_out(s)
|
|
188
453
|
|
|
189
|
-
async def
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
454
|
+
async def update_subscription(
|
|
455
|
+
self, provider_subscription_id: str, data: SubscriptionUpdateIn
|
|
456
|
+
) -> SubscriptionOut:
|
|
457
|
+
s = await _acall(stripe.Subscription.retrieve, provider_subscription_id, expand=["items"])
|
|
458
|
+
items = s.items.data
|
|
459
|
+
update_kwargs: dict[str, Any] = {"proration_behavior": data.proration_behavior}
|
|
460
|
+
# update first item (simple plan model)
|
|
461
|
+
if items:
|
|
462
|
+
first_item = items[0]
|
|
463
|
+
item_update = {"id": first_item.id}
|
|
464
|
+
if data.price_provider_id:
|
|
465
|
+
item_update["price"] = data.price_provider_id
|
|
466
|
+
if data.quantity is not None:
|
|
467
|
+
item_update["quantity"] = int(data.quantity)
|
|
468
|
+
update_kwargs["items"] = [item_update]
|
|
469
|
+
if data.cancel_at_period_end is not None:
|
|
470
|
+
update_kwargs["cancel_at_period_end"] = bool(data.cancel_at_period_end)
|
|
471
|
+
s2 = await _acall(stripe.Subscription.modify, provider_subscription_id, **update_kwargs)
|
|
472
|
+
return _sub_to_out(s2)
|
|
473
|
+
|
|
474
|
+
async def cancel_subscription(
|
|
475
|
+
self, provider_subscription_id: str, at_period_end: bool = True
|
|
476
|
+
) -> SubscriptionOut:
|
|
477
|
+
s = await _acall(
|
|
478
|
+
stripe.Subscription.cancel if not at_period_end else stripe.Subscription.modify,
|
|
479
|
+
provider_subscription_id,
|
|
480
|
+
**({} if not at_period_end else {"cancel_at_period_end": True}),
|
|
200
481
|
)
|
|
482
|
+
return _sub_to_out(s)
|
|
201
483
|
|
|
202
|
-
async def
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if amount is not None:
|
|
206
|
-
kwargs["amount_to_capture"] = int(amount)
|
|
207
|
-
pi = await _acall(stripe.PaymentIntent.capture, provider_intent_id, **kwargs)
|
|
208
|
-
return _pi_to_out(pi)
|
|
484
|
+
async def get_subscription(self, provider_subscription_id: str) -> SubscriptionOut:
|
|
485
|
+
s = await _acall(stripe.Subscription.retrieve, provider_subscription_id, expand=["items"])
|
|
486
|
+
return _sub_to_out(s)
|
|
209
487
|
|
|
210
|
-
async def
|
|
488
|
+
async def list_subscriptions(
|
|
211
489
|
self,
|
|
212
490
|
*,
|
|
213
491
|
customer_provider_id: str | None,
|
|
214
492
|
status: str | None,
|
|
215
493
|
limit: int,
|
|
216
494
|
cursor: str | None,
|
|
217
|
-
) -> tuple[list[
|
|
495
|
+
) -> tuple[list[SubscriptionOut], str | None]:
|
|
218
496
|
params = {"limit": int(limit)}
|
|
219
497
|
if customer_provider_id:
|
|
220
498
|
params["customer"] = customer_provider_id
|
|
@@ -222,16 +500,40 @@ class StripeAdapter(ProviderAdapter):
|
|
|
222
500
|
params["status"] = status
|
|
223
501
|
if cursor:
|
|
224
502
|
params["starting_after"] = cursor
|
|
225
|
-
res = await _acall(stripe.
|
|
226
|
-
items = [
|
|
503
|
+
res = await _acall(stripe.Subscription.list, **params)
|
|
504
|
+
items = [_sub_to_out(s) for s in res.data]
|
|
227
505
|
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
228
506
|
return items, next_cursor
|
|
229
507
|
|
|
230
|
-
#
|
|
231
|
-
async def
|
|
232
|
-
|
|
508
|
+
# -------- Invoices --------
|
|
509
|
+
async def create_invoice(self, data: InvoiceCreateIn) -> InvoiceOut:
|
|
510
|
+
inv = await _acall(
|
|
511
|
+
stripe.Invoice.create,
|
|
233
512
|
customer=data.customer_provider_id,
|
|
234
|
-
|
|
513
|
+
auto_advance=bool(data.auto_advance),
|
|
514
|
+
)
|
|
515
|
+
return _inv_to_out(inv)
|
|
516
|
+
|
|
517
|
+
async def finalize_invoice(self, provider_invoice_id: str) -> InvoiceOut:
|
|
518
|
+
inv = await _acall(stripe.Invoice.finalize_invoice, provider_invoice_id)
|
|
519
|
+
return _inv_to_out(inv)
|
|
520
|
+
|
|
521
|
+
async def void_invoice(self, provider_invoice_id: str) -> InvoiceOut:
|
|
522
|
+
inv = await _acall(stripe.Invoice.void_invoice, provider_invoice_id)
|
|
523
|
+
return _inv_to_out(inv)
|
|
524
|
+
|
|
525
|
+
async def pay_invoice(self, provider_invoice_id: str) -> InvoiceOut:
|
|
526
|
+
inv = await _acall(stripe.Invoice.pay, provider_invoice_id)
|
|
527
|
+
return _inv_to_out(inv)
|
|
528
|
+
|
|
529
|
+
async def add_invoice_line_item(
|
|
530
|
+
self, provider_invoice_id: str, data: InvoiceLineItemIn
|
|
531
|
+
) -> InvoiceOut:
|
|
532
|
+
# attach an item to a DRAFT invoice
|
|
533
|
+
kwargs: dict[str, Any] = dict(
|
|
534
|
+
invoice=provider_invoice_id,
|
|
535
|
+
customer=data.customer_provider_id,
|
|
536
|
+
quantity=int(data.quantity or 1),
|
|
235
537
|
currency=data.currency.lower(),
|
|
236
538
|
description=data.description or None,
|
|
237
539
|
)
|
|
@@ -239,10 +541,11 @@ class StripeAdapter(ProviderAdapter):
|
|
|
239
541
|
kwargs["price"] = data.provider_price_id
|
|
240
542
|
else:
|
|
241
543
|
kwargs["unit_amount"] = int(data.unit_amount)
|
|
242
|
-
|
|
544
|
+
await _acall(
|
|
243
545
|
stripe.InvoiceItem.create, **{k: v for k, v in kwargs.items() if v is not None}
|
|
244
546
|
)
|
|
245
|
-
|
|
547
|
+
inv = await _acall(stripe.Invoice.retrieve, provider_invoice_id)
|
|
548
|
+
return _inv_to_out(inv)
|
|
246
549
|
|
|
247
550
|
async def list_invoices(
|
|
248
551
|
self,
|
|
@@ -277,21 +580,229 @@ class StripeAdapter(ProviderAdapter):
|
|
|
277
580
|
inv = await _acall(stripe.Invoice.upcoming, **params)
|
|
278
581
|
return _inv_to_out(inv)
|
|
279
582
|
|
|
280
|
-
|
|
281
|
-
|
|
583
|
+
async def list_invoice_line_items(
|
|
584
|
+
self, provider_invoice_id: str, *, limit: int, cursor: str | None
|
|
585
|
+
) -> tuple[list[InvoiceLineItemOut], str | None]:
|
|
586
|
+
params = {"limit": int(limit)}
|
|
587
|
+
if cursor:
|
|
588
|
+
params["starting_after"] = cursor
|
|
589
|
+
res = await _acall(stripe.Invoice.list_lines, provider_invoice_id, **params)
|
|
590
|
+
items: list[InvoiceLineItemOut] = []
|
|
591
|
+
for li in res.data:
|
|
592
|
+
amount = int(getattr(li, "amount", 0))
|
|
593
|
+
currency = str(getattr(li, "currency", "USD")).upper()
|
|
594
|
+
price_id = getattr(getattr(li, "price", None), "id", None)
|
|
595
|
+
items.append(
|
|
596
|
+
InvoiceLineItemOut(
|
|
597
|
+
id=li.id,
|
|
598
|
+
description=getattr(li, "description", None),
|
|
599
|
+
amount=amount,
|
|
600
|
+
currency=currency,
|
|
601
|
+
quantity=getattr(li, "quantity", 1),
|
|
602
|
+
provider_price_id=price_id,
|
|
603
|
+
)
|
|
604
|
+
)
|
|
605
|
+
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
606
|
+
return items, next_cursor
|
|
607
|
+
|
|
608
|
+
# -------- Intents --------
|
|
609
|
+
async def create_intent(self, data: IntentCreateIn, *, user_id: str | None) -> IntentOut:
|
|
610
|
+
kwargs: dict[str, Any] = dict(
|
|
611
|
+
amount=int(data.amount),
|
|
612
|
+
currency=data.currency.lower(),
|
|
613
|
+
description=data.description or None,
|
|
614
|
+
capture_method="manual" if data.capture_method == "manual" else "automatic",
|
|
615
|
+
automatic_payment_methods={"enabled": True} if not data.payment_method_types else None,
|
|
616
|
+
)
|
|
617
|
+
if data.payment_method_types:
|
|
618
|
+
kwargs["payment_method_types"] = data.payment_method_types
|
|
619
|
+
pi = await _acall(
|
|
620
|
+
stripe.PaymentIntent.create, **{k: v for k, v in kwargs.items() if v is not None}
|
|
621
|
+
)
|
|
622
|
+
return _pi_to_out(pi)
|
|
623
|
+
|
|
624
|
+
async def confirm_intent(self, provider_intent_id: str) -> IntentOut:
|
|
625
|
+
pi = await _acall(stripe.PaymentIntent.confirm, provider_intent_id)
|
|
626
|
+
return _pi_to_out(pi)
|
|
627
|
+
|
|
628
|
+
async def cancel_intent(self, provider_intent_id: str) -> IntentOut:
|
|
629
|
+
pi = await _acall(stripe.PaymentIntent.cancel, provider_intent_id)
|
|
630
|
+
return _pi_to_out(pi)
|
|
631
|
+
|
|
632
|
+
async def refund(self, provider_intent_id: str, data: RefundIn) -> IntentOut:
|
|
633
|
+
pi = await _acall(
|
|
634
|
+
stripe.PaymentIntent.retrieve, provider_intent_id, expand=["latest_charge"]
|
|
635
|
+
)
|
|
636
|
+
charge_id = pi.latest_charge.id if getattr(pi, "latest_charge", None) else None
|
|
637
|
+
if not charge_id:
|
|
638
|
+
raise ValueError("No charge available to refund")
|
|
639
|
+
await _acall(
|
|
640
|
+
stripe.Refund.create,
|
|
641
|
+
charge=charge_id,
|
|
642
|
+
amount=int(data.amount) if data.amount else None,
|
|
643
|
+
reason=data.reason or None,
|
|
644
|
+
)
|
|
645
|
+
return await self.hydrate_intent(provider_intent_id)
|
|
646
|
+
|
|
647
|
+
async def hydrate_intent(self, provider_intent_id: str) -> IntentOut:
|
|
648
|
+
pi = await _acall(stripe.PaymentIntent.retrieve, provider_intent_id)
|
|
649
|
+
return _pi_to_out(pi)
|
|
650
|
+
|
|
651
|
+
async def capture_intent(self, provider_intent_id: str, *, amount: int | None) -> IntentOut:
|
|
652
|
+
kwargs = {}
|
|
653
|
+
if amount is not None:
|
|
654
|
+
kwargs["amount_to_capture"] = int(amount)
|
|
655
|
+
pi = await _acall(stripe.PaymentIntent.capture, provider_intent_id, **kwargs)
|
|
656
|
+
return _pi_to_out(pi)
|
|
657
|
+
|
|
658
|
+
async def list_intents(
|
|
659
|
+
self,
|
|
660
|
+
*,
|
|
661
|
+
customer_provider_id: str | None,
|
|
662
|
+
status: str | None,
|
|
663
|
+
limit: int,
|
|
664
|
+
cursor: str | None,
|
|
665
|
+
) -> tuple[list[IntentOut], str | None]:
|
|
666
|
+
params = {"limit": int(limit)}
|
|
667
|
+
if customer_provider_id:
|
|
668
|
+
params["customer"] = customer_provider_id
|
|
669
|
+
if status:
|
|
670
|
+
params["status"] = status
|
|
671
|
+
if cursor:
|
|
672
|
+
params["starting_after"] = cursor
|
|
673
|
+
res = await _acall(stripe.PaymentIntent.list, **params)
|
|
674
|
+
items = [_pi_to_out(pi) for pi in res.data]
|
|
675
|
+
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
676
|
+
return items, next_cursor
|
|
677
|
+
|
|
678
|
+
# ---- Setup Intents (off-session readiness) ----
|
|
679
|
+
async def create_setup_intent(self, data: SetupIntentCreateIn) -> SetupIntentOut:
|
|
680
|
+
si = await _acall(
|
|
681
|
+
stripe.SetupIntent.create, payment_method_types=data.payment_method_types or ["card"]
|
|
682
|
+
)
|
|
683
|
+
return SetupIntentOut(
|
|
684
|
+
id=si.id,
|
|
685
|
+
provider="stripe",
|
|
686
|
+
provider_setup_intent_id=si.id,
|
|
687
|
+
status=si.status,
|
|
688
|
+
client_secret=getattr(si, "client_secret", None),
|
|
689
|
+
next_action=NextAction(type=getattr(getattr(si, "next_action", None), "type", None)),
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
async def confirm_setup_intent(self, provider_setup_intent_id: str) -> SetupIntentOut:
|
|
693
|
+
si = await _acall(stripe.SetupIntent.confirm, provider_setup_intent_id)
|
|
694
|
+
return SetupIntentOut(
|
|
695
|
+
id=si.id,
|
|
696
|
+
provider="stripe",
|
|
697
|
+
provider_setup_intent_id=si.id,
|
|
698
|
+
status=si.status,
|
|
699
|
+
client_secret=getattr(si, "client_secret", None),
|
|
700
|
+
next_action=NextAction(type=getattr(getattr(si, "next_action", None), "type", None)),
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
async def get_setup_intent(self, provider_setup_intent_id: str) -> SetupIntentOut:
|
|
704
|
+
si = await _acall(stripe.SetupIntent.retrieve, provider_setup_intent_id)
|
|
705
|
+
return SetupIntentOut(
|
|
706
|
+
id=si.id,
|
|
707
|
+
provider="stripe",
|
|
708
|
+
provider_setup_intent_id=si.id,
|
|
709
|
+
status=si.status,
|
|
710
|
+
client_secret=getattr(si, "client_secret", None),
|
|
711
|
+
next_action=NextAction(type=getattr(getattr(si, "next_action", None), "type", None)),
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
# ---- 3DS/SCA resume ----
|
|
715
|
+
async def resume_intent_after_action(self, provider_intent_id: str) -> IntentOut:
|
|
716
|
+
# For Stripe, retrieving after the customer completes next_action is sufficient
|
|
717
|
+
return await self.hydrate_intent(provider_intent_id)
|
|
718
|
+
|
|
719
|
+
# -------- Disputes --------
|
|
720
|
+
async def list_disputes(
|
|
721
|
+
self, *, status: str | None, limit: int, cursor: str | None
|
|
722
|
+
) -> tuple[list[DisputeOut], str | None]:
|
|
723
|
+
params = {"limit": int(limit)}
|
|
724
|
+
if status:
|
|
725
|
+
params["status"] = status
|
|
726
|
+
if cursor:
|
|
727
|
+
params["starting_after"] = cursor
|
|
728
|
+
res = await _acall(stripe.Dispute.list, **params)
|
|
729
|
+
items = [_dispute_to_out(d) for d in res.data]
|
|
730
|
+
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
731
|
+
return items, next_cursor
|
|
732
|
+
|
|
733
|
+
async def get_dispute(self, provider_dispute_id: str) -> DisputeOut:
|
|
734
|
+
d = await _acall(stripe.Dispute.retrieve, provider_dispute_id)
|
|
735
|
+
return _dispute_to_out(d)
|
|
736
|
+
|
|
737
|
+
async def submit_dispute_evidence(self, provider_dispute_id: str, evidence: dict) -> DisputeOut:
|
|
738
|
+
d = await _acall(stripe.Dispute.modify, provider_dispute_id, evidence=evidence)
|
|
739
|
+
# Some disputes require explicit submit call:
|
|
740
|
+
try:
|
|
741
|
+
d = await _acall(stripe.Dispute.submit, provider_dispute_id)
|
|
742
|
+
except Exception:
|
|
743
|
+
pass
|
|
744
|
+
return _dispute_to_out(d)
|
|
745
|
+
|
|
746
|
+
# -------- Balance & Payouts --------
|
|
747
|
+
async def get_balance_snapshot(self) -> BalanceSnapshotOut:
|
|
748
|
+
bal = await _acall(stripe.Balance.retrieve)
|
|
749
|
+
|
|
750
|
+
def _bucket(entries):
|
|
751
|
+
out = []
|
|
752
|
+
for b in entries or []:
|
|
753
|
+
out.append({"currency": str(b.currency).upper(), "amount": int(b.amount)})
|
|
754
|
+
return out
|
|
755
|
+
|
|
756
|
+
return BalanceSnapshotOut(
|
|
757
|
+
available=_bucket(getattr(bal, "available", [])),
|
|
758
|
+
pending=_bucket(getattr(bal, "pending", [])),
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
async def list_payouts(
|
|
762
|
+
self, *, limit: int, cursor: str | None
|
|
763
|
+
) -> tuple[list[PayoutOut], str | None]:
|
|
764
|
+
params = {"limit": int(limit)}
|
|
765
|
+
if cursor:
|
|
766
|
+
params["starting_after"] = cursor
|
|
767
|
+
res = await _acall(stripe.Payout.list, **params)
|
|
768
|
+
items = [_payout_to_out(p) for p in res.data]
|
|
769
|
+
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
770
|
+
return items, next_cursor
|
|
771
|
+
|
|
772
|
+
async def get_payout(self, provider_payout_id: str) -> PayoutOut:
|
|
773
|
+
p = await _acall(stripe.Payout.retrieve, provider_payout_id)
|
|
774
|
+
return _payout_to_out(p)
|
|
775
|
+
|
|
776
|
+
# -------- Refunds (list/get) --------
|
|
777
|
+
async def list_refunds(
|
|
778
|
+
self, *, provider_payment_intent_id: str | None, limit: int, cursor: str | None
|
|
779
|
+
) -> tuple[list[RefundOut], str | None]:
|
|
780
|
+
params = {"limit": int(limit)}
|
|
781
|
+
if provider_payment_intent_id:
|
|
782
|
+
params["payment_intent"] = provider_payment_intent_id
|
|
783
|
+
if cursor:
|
|
784
|
+
params["starting_after"] = cursor
|
|
785
|
+
res = await _acall(stripe.Refund.list, **params)
|
|
786
|
+
items = [_refund_to_out(r) for r in res.data]
|
|
787
|
+
next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
|
|
788
|
+
return items, next_cursor
|
|
789
|
+
|
|
790
|
+
async def get_refund(self, provider_refund_id: str) -> RefundOut:
|
|
791
|
+
r = await _acall(stripe.Refund.retrieve, provider_refund_id)
|
|
792
|
+
return _refund_to_out(r)
|
|
793
|
+
|
|
794
|
+
# -------- Usage (create/list/get) --------
|
|
795
|
+
async def create_usage_record(self, data: UsageRecordIn) -> UsageRecordOut:
|
|
282
796
|
if not data.subscription_item and not data.provider_price_id:
|
|
283
797
|
raise ValueError("subscription_item or provider_price_id is required")
|
|
284
|
-
# If a price is given, you’d normally look up the active subscription_item for that price.
|
|
285
798
|
sub_item = data.subscription_item
|
|
286
799
|
if not sub_item and data.provider_price_id:
|
|
287
|
-
# best-effort: find an active subscription item for the price
|
|
288
800
|
items = await _acall(
|
|
289
801
|
stripe.SubscriptionItem.list, price=data.provider_price_id, limit=1
|
|
290
802
|
)
|
|
291
803
|
sub_item = items.data[0].id if items.data else None
|
|
292
804
|
if not sub_item:
|
|
293
805
|
raise ValueError("No subscription item found for usage record")
|
|
294
|
-
|
|
295
806
|
body = {
|
|
296
807
|
"subscription_item": sub_item,
|
|
297
808
|
"quantity": int(data.quantity),
|
|
@@ -300,4 +811,63 @@ class StripeAdapter(ProviderAdapter):
|
|
|
300
811
|
if data.timestamp:
|
|
301
812
|
body["timestamp"] = int(data.timestamp)
|
|
302
813
|
rec = await _acall(stripe.UsageRecord.create, **body)
|
|
303
|
-
return
|
|
814
|
+
return UsageRecordOut(
|
|
815
|
+
id=rec.id,
|
|
816
|
+
quantity=int(rec.quantity),
|
|
817
|
+
timestamp=getattr(rec, "timestamp", None),
|
|
818
|
+
subscription_item=sub_item,
|
|
819
|
+
provider_price_id=data.provider_price_id,
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
async def list_usage_records(
|
|
823
|
+
self, f: UsageRecordListFilter
|
|
824
|
+
) -> tuple[list[UsageRecordOut], str | None]:
|
|
825
|
+
# Stripe exposes *summaries* per period. We surface them as list results.
|
|
826
|
+
sub_item = f.subscription_item
|
|
827
|
+
if not sub_item and f.provider_price_id:
|
|
828
|
+
items = await _acall(stripe.SubscriptionItem.list, price=f.provider_price_id, limit=1)
|
|
829
|
+
sub_item = items.data[0].id if items.data else None
|
|
830
|
+
if not sub_item:
|
|
831
|
+
return [], None
|
|
832
|
+
params = {"limit": int(f.limit or 50)}
|
|
833
|
+
if f.cursor:
|
|
834
|
+
params["starting_after"] = f.cursor
|
|
835
|
+
res = await _acall(stripe.SubscriptionItem.list_usage_record_summaries, sub_item, **params)
|
|
836
|
+
items: list[UsageRecordOut] = []
|
|
837
|
+
for s in res.data:
|
|
838
|
+
# No record id in summaries—synthesize a stable id from period start.
|
|
839
|
+
synthesized_id = f"{sub_item}:{getattr(s, 'period', {}).get('start')}"
|
|
840
|
+
items.append(
|
|
841
|
+
UsageRecordOut(
|
|
842
|
+
id=synthesized_id,
|
|
843
|
+
quantity=int(getattr(s, "total_usage", 0)),
|
|
844
|
+
timestamp=getattr(s, "period", {}).get("end"),
|
|
845
|
+
subscription_item=sub_item,
|
|
846
|
+
provider_price_id=f.provider_price_id,
|
|
847
|
+
)
|
|
848
|
+
)
|
|
849
|
+
next_cursor = (
|
|
850
|
+
res.data[-1].id
|
|
851
|
+
if getattr(res, "has_more", False) and res.data and hasattr(res.data[-1], "id")
|
|
852
|
+
else None
|
|
853
|
+
)
|
|
854
|
+
return items, next_cursor
|
|
855
|
+
|
|
856
|
+
async def get_usage_record(self, usage_record_id: str) -> UsageRecordOut:
|
|
857
|
+
# Stripe has no direct "retrieve usage record by id" API.
|
|
858
|
+
# You can reconstruct via list summaries or store records locally when creating.
|
|
859
|
+
raise NotImplementedError("Stripe does not support retrieving a single usage record by id")
|
|
860
|
+
|
|
861
|
+
# -------- Webhooks --------
|
|
862
|
+
async def verify_and_parse_webhook(
|
|
863
|
+
self, signature: str | None, payload: bytes
|
|
864
|
+
) -> dict[str, Any]:
|
|
865
|
+
if not self._wh_secret:
|
|
866
|
+
raise ValueError("Stripe webhook secret not configured")
|
|
867
|
+
event = await _acall(
|
|
868
|
+
stripe.Webhook.construct_event,
|
|
869
|
+
payload=payload,
|
|
870
|
+
sig_header=signature,
|
|
871
|
+
secret=self._wh_secret,
|
|
872
|
+
)
|
|
873
|
+
return {"id": event.id, "type": event.type, "data": event.data.object}
|
|
@@ -5,7 +5,7 @@ svc_infra/apf_payments/models.py,sha256=u4U5oszha5uulCIrNoajaFDIc5YmTlh2mtm-yJUv
|
|
|
5
5
|
svc_infra/apf_payments/provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
svc_infra/apf_payments/provider/base.py,sha256=1t5znglpGFhjt4zdbuzE5VlHvGarFwzH2oscK8yyKNY,7678
|
|
7
7
|
svc_infra/apf_payments/provider/registry.py,sha256=NZ4pUkFcbXNtqWEpFeI3NwoKRYGWe9fVQakmlrVLTKE,878
|
|
8
|
-
svc_infra/apf_payments/provider/stripe.py,sha256=
|
|
8
|
+
svc_infra/apf_payments/provider/stripe.py,sha256=Xb_UjdobbBzK-an9cO1jRWiP6OHvki5MDp6JnS6a1-I,34392
|
|
9
9
|
svc_infra/apf_payments/schemas.py,sha256=XfBxx6z_Y6cdHktanafNDhhgWl8JVvag9vp2ORmvn_4,8403
|
|
10
10
|
svc_infra/apf_payments/service.py,sha256=bn3BTOTdfkJ4b0Z9cHuFHvlMcv9B1b2n0v-unveUplA,31060
|
|
11
11
|
svc_infra/apf_payments/settings.py,sha256=VnNQbajbv843buUisqa82xOQ-f5JA8JzHD8o01-yhPQ,1239
|
|
@@ -228,7 +228,7 @@ svc_infra/obs/templates/sidecars/railway/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
|
|
|
228
228
|
svc_infra/obs/templates/sidecars/railway/agent.yaml,sha256=hYv35yG92XEP_4joMFmMcVTD-4fG_zHitmChjreUJh4,516
|
|
229
229
|
svc_infra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
230
230
|
svc_infra/utils.py,sha256=VX1yjTx61-YvAymyRhGy18DhybiVdPddiYD_FlKTbJU,952
|
|
231
|
-
svc_infra-0.1.
|
|
232
|
-
svc_infra-0.1.
|
|
233
|
-
svc_infra-0.1.
|
|
234
|
-
svc_infra-0.1.
|
|
231
|
+
svc_infra-0.1.587.dist-info/METADATA,sha256=hirPy7fbFdNdvqQWb1ycjYOl7sqPuJHgZPcQO4LDGf8,3487
|
|
232
|
+
svc_infra-0.1.587.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
233
|
+
svc_infra-0.1.587.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
|
|
234
|
+
svc_infra-0.1.587.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|