geek-cafe-saas-sdk 0.6.0__py3-none-any.whl → 0.7.1__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 geek-cafe-saas-sdk might be problematic. Click here for more details.
- geek_cafe_saas_sdk/__init__.py +2 -2
- geek_cafe_saas_sdk/domains/files/handlers/README.md +446 -0
- geek_cafe_saas_sdk/domains/files/handlers/__init__.py +6 -0
- geek_cafe_saas_sdk/domains/files/handlers/files/create/app.py +121 -0
- geek_cafe_saas_sdk/domains/files/handlers/files/download/app.py +80 -0
- geek_cafe_saas_sdk/domains/files/handlers/files/get/app.py +62 -0
- geek_cafe_saas_sdk/domains/files/handlers/files/list/app.py +72 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/create_derived/app.py +99 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/create_main/app.py +104 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/download_bundle/app.py +99 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/get_lineage/app.py +68 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/prepare_bundle/app.py +76 -0
- geek_cafe_saas_sdk/domains/files/models/__init__.py +17 -0
- geek_cafe_saas_sdk/domains/files/models/directory.py +42 -6
- geek_cafe_saas_sdk/domains/files/models/file.py +158 -16
- geek_cafe_saas_sdk/domains/files/models/file_share.py +33 -0
- geek_cafe_saas_sdk/domains/files/models/file_version.py +24 -0
- geek_cafe_saas_sdk/domains/files/services/__init__.py +21 -0
- geek_cafe_saas_sdk/domains/files/services/directory_service.py +54 -135
- geek_cafe_saas_sdk/domains/files/services/file_lineage_service.py +487 -0
- geek_cafe_saas_sdk/domains/files/services/file_share_service.py +37 -120
- geek_cafe_saas_sdk/domains/files/services/file_system_service.py +67 -103
- geek_cafe_saas_sdk/domains/files/services/file_version_service.py +44 -124
- geek_cafe_saas_sdk/domains/messaging/services/contact_thread_service.py +55 -7
- geek_cafe_saas_sdk/domains/notifications/__init__.py +18 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/__init__.py +1 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/create_webhook/app.py +73 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/get/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/get_preferences/app.py +34 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/list/app.py +43 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/list_webhooks/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/mark_read/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/send/app.py +83 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/update_preferences/app.py +45 -0
- geek_cafe_saas_sdk/domains/notifications/models/__init__.py +16 -0
- geek_cafe_saas_sdk/domains/notifications/models/notification.py +717 -0
- geek_cafe_saas_sdk/domains/notifications/models/notification_preference.py +365 -0
- geek_cafe_saas_sdk/domains/notifications/models/webhook_subscription.py +339 -0
- geek_cafe_saas_sdk/domains/notifications/services/__init__.py +10 -0
- geek_cafe_saas_sdk/domains/notifications/services/notification_service.py +576 -0
- geek_cafe_saas_sdk/domains/payments/__init__.py +16 -0
- geek_cafe_saas_sdk/domains/payments/handlers/README.md +334 -0
- geek_cafe_saas_sdk/domains/payments/handlers/__init__.py +6 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/create/app.py +105 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/update/app.py +97 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payment_intents/create/app.py +97 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payment_intents/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/list/app.py +68 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/record/app.py +118 -0
- geek_cafe_saas_sdk/domains/payments/handlers/refunds/create/app.py +89 -0
- geek_cafe_saas_sdk/domains/payments/handlers/refunds/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/models/__init__.py +17 -0
- geek_cafe_saas_sdk/domains/payments/models/billing_account.py +521 -0
- geek_cafe_saas_sdk/domains/payments/models/payment.py +639 -0
- geek_cafe_saas_sdk/domains/payments/models/payment_intent_ref.py +539 -0
- geek_cafe_saas_sdk/domains/payments/models/refund.py +404 -0
- geek_cafe_saas_sdk/domains/payments/services/__init__.py +11 -0
- geek_cafe_saas_sdk/domains/payments/services/payment_service.py +405 -0
- geek_cafe_saas_sdk/domains/subscriptions/__init__.py +19 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/README.md +408 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/__init__.py +1 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/create/app.py +81 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/get/app.py +48 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/list/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/update/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/create/app.py +83 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/get/app.py +47 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/validate/app.py +62 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/create/app.py +82 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/get/app.py +48 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/list/app.py +66 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/update/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/usage/aggregate/app.py +72 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/usage/record/app.py +89 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/__init__.py +13 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/addon.py +604 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/discount.py +492 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/plan.py +569 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/usage_record.py +300 -0
- geek_cafe_saas_sdk/domains/subscriptions/services/__init__.py +10 -0
- geek_cafe_saas_sdk/domains/subscriptions/services/subscription_manager_service.py +694 -0
- geek_cafe_saas_sdk/domains/tenancy/models/subscription.py +123 -1
- geek_cafe_saas_sdk/domains/tenancy/services/subscription_service.py +213 -0
- geek_cafe_saas_sdk/lambda_handlers/_base/base_handler.py +7 -0
- geek_cafe_saas_sdk/services/database_service.py +10 -6
- geek_cafe_saas_sdk/utilities/cognito_utility.py +16 -26
- geek_cafe_saas_sdk/utilities/environment_variables.py +16 -0
- geek_cafe_saas_sdk/utilities/logging_utility.py +77 -0
- {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/METADATA +11 -11
- {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/RECORD +94 -23
- {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/WHEEL +0 -0
- {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda handler for getting payment intent by ID.
|
|
3
|
+
|
|
4
|
+
Requires authentication (secure mode).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from geek_cafe_saas_sdk.lambda_handlers import create_handler
|
|
9
|
+
from geek_cafe_saas_sdk.domains.payments.services import PaymentService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Factory creates handler (defaults to secure auth)
|
|
13
|
+
handler_wrapper = create_handler(
|
|
14
|
+
service_class=PaymentService,
|
|
15
|
+
require_body=False,
|
|
16
|
+
convert_case=True
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
|
|
21
|
+
"""
|
|
22
|
+
Get payment intent by ID.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
event: Lambda event from API Gateway
|
|
26
|
+
context: Lambda context
|
|
27
|
+
injected_service: Optional PaymentService for testing
|
|
28
|
+
|
|
29
|
+
Path parameters:
|
|
30
|
+
- intentId: Payment intent reference ID
|
|
31
|
+
|
|
32
|
+
Returns 200 with payment intent data
|
|
33
|
+
"""
|
|
34
|
+
return handler_wrapper.execute(event, context, get_payment_intent, injected_service)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_payment_intent(
|
|
38
|
+
event: Dict[str, Any],
|
|
39
|
+
service: PaymentService,
|
|
40
|
+
user_context: Dict[str, str]
|
|
41
|
+
) -> Any:
|
|
42
|
+
"""
|
|
43
|
+
Business logic for getting payment intent.
|
|
44
|
+
"""
|
|
45
|
+
tenant_id = user_context.get("tenant_id")
|
|
46
|
+
|
|
47
|
+
# Extract intent ID from path parameters
|
|
48
|
+
path_params = event.get("pathParameters", {})
|
|
49
|
+
intent_id = path_params.get("intentId")
|
|
50
|
+
|
|
51
|
+
if not intent_id:
|
|
52
|
+
raise ValueError("intentId path parameter is required")
|
|
53
|
+
|
|
54
|
+
# Get payment intent
|
|
55
|
+
result = service.get_payment_intent(
|
|
56
|
+
intent_ref_id=intent_id,
|
|
57
|
+
tenant_id=tenant_id
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return result
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda handler for getting payment by ID.
|
|
3
|
+
|
|
4
|
+
Requires authentication (secure mode).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from geek_cafe_saas_sdk.lambda_handlers import create_handler
|
|
9
|
+
from geek_cafe_saas_sdk.domains.payments.services import PaymentService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Factory creates handler (defaults to secure auth)
|
|
13
|
+
handler_wrapper = create_handler(
|
|
14
|
+
service_class=PaymentService,
|
|
15
|
+
require_body=False,
|
|
16
|
+
convert_case=True
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
|
|
21
|
+
"""
|
|
22
|
+
Get payment by ID.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
event: Lambda event from API Gateway
|
|
26
|
+
context: Lambda context
|
|
27
|
+
injected_service: Optional PaymentService for testing
|
|
28
|
+
|
|
29
|
+
Path parameters:
|
|
30
|
+
- paymentId: Payment ID
|
|
31
|
+
|
|
32
|
+
Returns 200 with payment data
|
|
33
|
+
"""
|
|
34
|
+
return handler_wrapper.execute(event, context, get_payment, injected_service)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_payment(
|
|
38
|
+
event: Dict[str, Any],
|
|
39
|
+
service: PaymentService,
|
|
40
|
+
user_context: Dict[str, str]
|
|
41
|
+
) -> Any:
|
|
42
|
+
"""
|
|
43
|
+
Business logic for getting payment.
|
|
44
|
+
"""
|
|
45
|
+
tenant_id = user_context.get("tenant_id")
|
|
46
|
+
|
|
47
|
+
# Extract payment ID from path parameters
|
|
48
|
+
path_params = event.get("pathParameters", {})
|
|
49
|
+
payment_id = path_params.get("paymentId")
|
|
50
|
+
|
|
51
|
+
if not payment_id:
|
|
52
|
+
raise ValueError("paymentId path parameter is required")
|
|
53
|
+
|
|
54
|
+
# Get payment
|
|
55
|
+
result = service.get_payment(
|
|
56
|
+
payment_id=payment_id,
|
|
57
|
+
tenant_id=tenant_id
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return result
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda handler for listing payments.
|
|
3
|
+
|
|
4
|
+
Requires authentication (secure mode).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from geek_cafe_saas_sdk.lambda_handlers import create_handler
|
|
9
|
+
from geek_cafe_saas_sdk.domains.payments.services import PaymentService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Factory creates handler (defaults to secure auth)
|
|
13
|
+
handler_wrapper = create_handler(
|
|
14
|
+
service_class=PaymentService,
|
|
15
|
+
require_body=False,
|
|
16
|
+
convert_case=True
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
|
|
21
|
+
"""
|
|
22
|
+
List payments for a tenant or billing account.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
event: Lambda event from API Gateway
|
|
26
|
+
context: Lambda context
|
|
27
|
+
injected_service: Optional PaymentService for testing
|
|
28
|
+
|
|
29
|
+
Query parameters:
|
|
30
|
+
- billingAccountId: Optional billing account ID to filter by
|
|
31
|
+
- limit: Optional page size (default: 50, max: 100)
|
|
32
|
+
|
|
33
|
+
Returns 200 with list of payments
|
|
34
|
+
"""
|
|
35
|
+
return handler_wrapper.execute(event, context, list_payments, injected_service)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def list_payments(
|
|
39
|
+
event: Dict[str, Any],
|
|
40
|
+
service: PaymentService,
|
|
41
|
+
user_context: Dict[str, str]
|
|
42
|
+
) -> Any:
|
|
43
|
+
"""
|
|
44
|
+
Business logic for listing payments.
|
|
45
|
+
"""
|
|
46
|
+
tenant_id = user_context.get("tenant_id")
|
|
47
|
+
|
|
48
|
+
# Extract query parameters
|
|
49
|
+
query_params = event.get("queryStringParameters") or {}
|
|
50
|
+
billing_account_id = query_params.get("billingAccountId")
|
|
51
|
+
limit = query_params.get("limit", "50")
|
|
52
|
+
|
|
53
|
+
# Validate and convert limit
|
|
54
|
+
try:
|
|
55
|
+
limit = int(limit)
|
|
56
|
+
if limit < 1 or limit > 100:
|
|
57
|
+
raise ValueError("limit must be between 1 and 100")
|
|
58
|
+
except ValueError as e:
|
|
59
|
+
raise ValueError(f"Invalid limit parameter: {str(e)}")
|
|
60
|
+
|
|
61
|
+
# List payments
|
|
62
|
+
result = service.list_payments(
|
|
63
|
+
tenant_id=tenant_id,
|
|
64
|
+
billing_account_id=billing_account_id,
|
|
65
|
+
limit=limit
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return result
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda handler for recording settled payments.
|
|
3
|
+
|
|
4
|
+
Typically called from Stripe webhooks or payment processor callbacks.
|
|
5
|
+
Requires authentication (secure mode).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
from geek_cafe_saas_sdk.lambda_handlers import create_handler
|
|
10
|
+
from geek_cafe_saas_sdk.domains.payments.services import PaymentService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Factory creates handler (defaults to secure auth)
|
|
14
|
+
handler_wrapper = create_handler(
|
|
15
|
+
service_class=PaymentService,
|
|
16
|
+
require_body=True,
|
|
17
|
+
convert_case=True
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
|
|
22
|
+
"""
|
|
23
|
+
Record a settled payment.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
event: Lambda event from API Gateway or webhook
|
|
27
|
+
context: Lambda context
|
|
28
|
+
injected_service: Optional PaymentService for testing
|
|
29
|
+
|
|
30
|
+
Expected body (camelCase from frontend/webhook):
|
|
31
|
+
{
|
|
32
|
+
"billingAccountId": "account-123",
|
|
33
|
+
"paymentIntentRefId": "intent-123", // Optional
|
|
34
|
+
"grossAmountCents": 5000, // $50.00
|
|
35
|
+
"feeAmountCents": 145, // $1.45 (Stripe fee)
|
|
36
|
+
"currencyCode": "USD",
|
|
37
|
+
"pspType": "stripe",
|
|
38
|
+
"pspTransactionId": "txn_xxx",
|
|
39
|
+
"pspChargeId": "ch_xxx",
|
|
40
|
+
"pspBalanceTransactionId": "txn_balance_xxx",
|
|
41
|
+
"paymentMethodId": "pm_xxx",
|
|
42
|
+
"paymentMethodType": "card",
|
|
43
|
+
"paymentMethodLast4": "4242",
|
|
44
|
+
"paymentMethodBrand": "visa",
|
|
45
|
+
"paymentMethodFunding": "credit",
|
|
46
|
+
"description": "Subscription payment",
|
|
47
|
+
"statementDescriptor": "ACME SUBSCRIPTION",
|
|
48
|
+
"receiptEmail": "user@example.com",
|
|
49
|
+
"receiptUrl": "https://stripe.com/receipt/xxx",
|
|
50
|
+
"customerId": "user-123", // Optional
|
|
51
|
+
"invoiceId": "inv-123", // Optional
|
|
52
|
+
"subscriptionId": "sub-123" // Optional
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Returns 201 with recorded payment
|
|
56
|
+
"""
|
|
57
|
+
return handler_wrapper.execute(event, context, record_payment, injected_service)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def record_payment(
|
|
61
|
+
event: Dict[str, Any],
|
|
62
|
+
service: PaymentService,
|
|
63
|
+
user_context: Dict[str, str]
|
|
64
|
+
) -> Any:
|
|
65
|
+
"""
|
|
66
|
+
Business logic for recording payments.
|
|
67
|
+
"""
|
|
68
|
+
payload = event["parsed_body"]
|
|
69
|
+
tenant_id = user_context.get("tenant_id")
|
|
70
|
+
|
|
71
|
+
# Extract required fields
|
|
72
|
+
billing_account_id = payload.get("billing_account_id")
|
|
73
|
+
gross_amount_cents = payload.get("gross_amount_cents")
|
|
74
|
+
fee_amount_cents = payload.get("fee_amount_cents")
|
|
75
|
+
|
|
76
|
+
if not billing_account_id:
|
|
77
|
+
raise ValueError("billing_account_id is required")
|
|
78
|
+
if gross_amount_cents is None or gross_amount_cents <= 0:
|
|
79
|
+
raise ValueError("gross_amount_cents must be greater than 0")
|
|
80
|
+
if fee_amount_cents is None or fee_amount_cents < 0:
|
|
81
|
+
raise ValueError("fee_amount_cents must be non-negative")
|
|
82
|
+
|
|
83
|
+
# Extract optional fields
|
|
84
|
+
payment_intent_ref_id = payload.get("payment_intent_ref_id")
|
|
85
|
+
currency_code = payload.get("currency_code", "USD")
|
|
86
|
+
psp_type = payload.get("psp_type", "stripe")
|
|
87
|
+
|
|
88
|
+
# Build kwargs for optional fields
|
|
89
|
+
kwargs = {}
|
|
90
|
+
optional_fields = [
|
|
91
|
+
"psp_transaction_id", "psp_charge_id", "psp_balance_transaction_id",
|
|
92
|
+
"fee_details",
|
|
93
|
+
"payment_method_id", "payment_method_type", "payment_method_last4",
|
|
94
|
+
"payment_method_brand", "payment_method_funding",
|
|
95
|
+
"settlement_date", "payout_id",
|
|
96
|
+
"customer_id", "invoice_id", "subscription_id",
|
|
97
|
+
"description", "statement_descriptor",
|
|
98
|
+
"receipt_number", "receipt_email", "receipt_url",
|
|
99
|
+
"psp_metadata", "application_fee_amount_cents"
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
for field in optional_fields:
|
|
103
|
+
if field in payload:
|
|
104
|
+
kwargs[field] = payload[field]
|
|
105
|
+
|
|
106
|
+
# Record payment
|
|
107
|
+
result = service.record_payment(
|
|
108
|
+
tenant_id=tenant_id,
|
|
109
|
+
billing_account_id=billing_account_id,
|
|
110
|
+
payment_intent_ref_id=payment_intent_ref_id,
|
|
111
|
+
gross_amount_cents=gross_amount_cents,
|
|
112
|
+
fee_amount_cents=fee_amount_cents,
|
|
113
|
+
currency_code=currency_code,
|
|
114
|
+
psp_type=psp_type,
|
|
115
|
+
**kwargs
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return result
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda handler for creating refunds.
|
|
3
|
+
|
|
4
|
+
Requires authentication (secure mode).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from geek_cafe_saas_sdk.lambda_handlers import create_handler
|
|
9
|
+
from geek_cafe_saas_sdk.domains.payments.services import PaymentService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Factory creates handler (defaults to secure auth)
|
|
13
|
+
handler_wrapper = create_handler(
|
|
14
|
+
service_class=PaymentService,
|
|
15
|
+
require_body=True,
|
|
16
|
+
convert_case=True
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
|
|
21
|
+
"""
|
|
22
|
+
Create a refund for a payment.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
event: Lambda event from API Gateway
|
|
26
|
+
context: Lambda context
|
|
27
|
+
injected_service: Optional PaymentService for testing
|
|
28
|
+
|
|
29
|
+
Expected body (camelCase from frontend):
|
|
30
|
+
{
|
|
31
|
+
"paymentId": "payment-123",
|
|
32
|
+
"amountCents": 5000, // Amount to refund (must not exceed remaining)
|
|
33
|
+
"reason": "requested_by_customer", // "duplicate", "fraudulent", "requested_by_customer"
|
|
34
|
+
"description": "Customer requested refund",
|
|
35
|
+
"pspRefundId": "re_xxx", // Optional - PSP refund ID if already created
|
|
36
|
+
"disputeId": "dp_xxx" // Optional - if related to dispute
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Returns 201 with created refund
|
|
40
|
+
"""
|
|
41
|
+
return handler_wrapper.execute(event, context, create_refund, injected_service)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def create_refund(
|
|
45
|
+
event: Dict[str, Any],
|
|
46
|
+
service: PaymentService,
|
|
47
|
+
user_context: Dict[str, str]
|
|
48
|
+
) -> Any:
|
|
49
|
+
"""
|
|
50
|
+
Business logic for creating refunds.
|
|
51
|
+
"""
|
|
52
|
+
payload = event["parsed_body"]
|
|
53
|
+
tenant_id = user_context.get("tenant_id")
|
|
54
|
+
user_id = user_context.get("user_id")
|
|
55
|
+
|
|
56
|
+
# Extract required fields
|
|
57
|
+
payment_id = payload.get("payment_id")
|
|
58
|
+
amount_cents = payload.get("amount_cents")
|
|
59
|
+
|
|
60
|
+
if not payment_id:
|
|
61
|
+
raise ValueError("payment_id is required")
|
|
62
|
+
if amount_cents is None or amount_cents <= 0:
|
|
63
|
+
raise ValueError("amount_cents must be greater than 0")
|
|
64
|
+
|
|
65
|
+
# Extract optional fields
|
|
66
|
+
reason = payload.get("reason")
|
|
67
|
+
|
|
68
|
+
# Build kwargs for optional fields
|
|
69
|
+
kwargs = {}
|
|
70
|
+
optional_fields = [
|
|
71
|
+
"description", "psp_refund_id", "psp_balance_transaction_id",
|
|
72
|
+
"dispute_id", "notes"
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
for field in optional_fields:
|
|
76
|
+
if field in payload:
|
|
77
|
+
kwargs[field] = payload[field]
|
|
78
|
+
|
|
79
|
+
# Create refund
|
|
80
|
+
result = service.create_refund(
|
|
81
|
+
tenant_id=tenant_id,
|
|
82
|
+
payment_id=payment_id,
|
|
83
|
+
amount_cents=amount_cents,
|
|
84
|
+
reason=reason,
|
|
85
|
+
initiated_by_id=user_id,
|
|
86
|
+
**kwargs
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return result
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda handler for getting refund by ID.
|
|
3
|
+
|
|
4
|
+
Requires authentication (secure mode).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from geek_cafe_saas_sdk.lambda_handlers import create_handler
|
|
9
|
+
from geek_cafe_saas_sdk.domains.payments.services import PaymentService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Factory creates handler (defaults to secure auth)
|
|
13
|
+
handler_wrapper = create_handler(
|
|
14
|
+
service_class=PaymentService,
|
|
15
|
+
require_body=False,
|
|
16
|
+
convert_case=True
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
|
|
21
|
+
"""
|
|
22
|
+
Get refund by ID.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
event: Lambda event from API Gateway
|
|
26
|
+
context: Lambda context
|
|
27
|
+
injected_service: Optional PaymentService for testing
|
|
28
|
+
|
|
29
|
+
Path parameters:
|
|
30
|
+
- refundId: Refund ID
|
|
31
|
+
|
|
32
|
+
Returns 200 with refund data
|
|
33
|
+
"""
|
|
34
|
+
return handler_wrapper.execute(event, context, get_refund, injected_service)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_refund(
|
|
38
|
+
event: Dict[str, Any],
|
|
39
|
+
service: PaymentService,
|
|
40
|
+
user_context: Dict[str, str]
|
|
41
|
+
) -> Any:
|
|
42
|
+
"""
|
|
43
|
+
Business logic for getting refund.
|
|
44
|
+
"""
|
|
45
|
+
tenant_id = user_context.get("tenant_id")
|
|
46
|
+
|
|
47
|
+
# Extract refund ID from path parameters
|
|
48
|
+
path_params = event.get("pathParameters", {})
|
|
49
|
+
refund_id = path_params.get("refundId")
|
|
50
|
+
|
|
51
|
+
if not refund_id:
|
|
52
|
+
raise ValueError("refundId path parameter is required")
|
|
53
|
+
|
|
54
|
+
# Get refund
|
|
55
|
+
result = service.get_refund(
|
|
56
|
+
refund_id=refund_id,
|
|
57
|
+
tenant_id=tenant_id
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return result
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Payment models.
|
|
2
|
+
|
|
3
|
+
Geek Cafe, LLC
|
|
4
|
+
MIT License. See Project Root for the license information.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .billing_account import BillingAccount
|
|
8
|
+
from .payment_intent_ref import PaymentIntentRef
|
|
9
|
+
from .payment import Payment
|
|
10
|
+
from .refund import Refund
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"BillingAccount",
|
|
14
|
+
"PaymentIntentRef",
|
|
15
|
+
"Payment",
|
|
16
|
+
"Refund",
|
|
17
|
+
]
|