wip checkout
This commit is contained in:
@@ -5,6 +5,9 @@ ORDER_TABLE: str = os.getenv('ORDER_TABLE') # type: ignore
|
||||
COURSE_TABLE: str = os.getenv('COURSE_TABLE') # type: ignore
|
||||
ENROLLMENT_TABLE: str = os.getenv('ENROLLMENT_TABLE') # type: ignore
|
||||
|
||||
IUGU_ACCOUNT_ID: str = 'AF01CF1B3451459F92666F10589278EE'
|
||||
IUGU_API_TOKEN: str = os.getenv('IUGU_API_TOKEN') # type: ignore
|
||||
|
||||
BUCKET_NAME: str = os.getenv('BUCKET_NAME') # type: ignore
|
||||
|
||||
EMAIL_SENDER = ('EDUSEG®', 'noreply@eduseg.com.br')
|
||||
|
||||
@@ -57,14 +57,15 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
},
|
||||
)
|
||||
|
||||
transact.update(
|
||||
key=KeyPair(new_image['id'], 'author'),
|
||||
update_expr='SET user_id = :user_id, updated_at = :updated_at',
|
||||
expr_attr_values={
|
||||
':user_id': r['user_id'],
|
||||
':updated_at': now_,
|
||||
},
|
||||
)
|
||||
if 'org_id' not in new_image:
|
||||
transact.update(
|
||||
key=KeyPair(new_image['id'], 'author'),
|
||||
update_expr='SET user_id = :user_id, updated_at = :updated_at',
|
||||
expr_attr_values={
|
||||
':user_id': r['user_id'],
|
||||
':updated_at': now_,
|
||||
},
|
||||
)
|
||||
|
||||
logger.info('IDs updated')
|
||||
|
||||
|
||||
@@ -16,36 +16,66 @@ Documentation:
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
from urllib.parse import ParseResult, urlparse
|
||||
|
||||
import requests
|
||||
from aws_lambda_powertools import Logger
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
# from data_classes.invoice import Invoice, Pix
|
||||
# from data_classes.order import CreditCard, Order, PaymentMethod, Status
|
||||
from layercake.extra_types import CreditCard
|
||||
from pydantic import BaseModel, ConfigDict, HttpUrl
|
||||
|
||||
logger = Logger(__name__)
|
||||
|
||||
|
||||
class Status(Enum):
|
||||
class Status(str, Enum):
|
||||
PAID = 'PAID'
|
||||
DECLINED = 'DECLINED'
|
||||
|
||||
|
||||
class PaymentMethod(Enum):
|
||||
class PaymentMethod(str, Enum):
|
||||
PIX = 'PIX'
|
||||
BANK_SLIP = 'BANK_SLIP'
|
||||
CREDIT_CARD = 'CREDIT_CARD'
|
||||
|
||||
|
||||
class Order: ...
|
||||
@dataclass
|
||||
class Address(BaseModel):
|
||||
postcode: str
|
||||
neighborhood: str
|
||||
city: str
|
||||
state: str
|
||||
address1: str
|
||||
address2: str | None = None
|
||||
|
||||
|
||||
class CreditCard: ...
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
quantity: int = 1
|
||||
unit_price: Decimal
|
||||
|
||||
|
||||
class Order(BaseModel):
|
||||
model_config = ConfigDict(use_enum_values=True)
|
||||
|
||||
id: str
|
||||
email: str
|
||||
name: str
|
||||
due_date: datetime
|
||||
address: Address
|
||||
items: tuple[Item, ...]
|
||||
payment_method: PaymentMethod
|
||||
cpf: str | None = None
|
||||
cnpj: str | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Invoice: ...
|
||||
|
||||
|
||||
@dataclass
|
||||
class BankSlip(BaseModel):
|
||||
digitable_line: str
|
||||
bank_slip_url: HttpUrl
|
||||
@@ -115,7 +145,7 @@ class Iugu:
|
||||
payload = {
|
||||
'order_id': order.id,
|
||||
'external_reference': order.id,
|
||||
'due_date': order.due_date.strftime('%Y-%m-%d'), # type: ignore
|
||||
'due_date': order.due_date.strftime('%Y-%m-%d'),
|
||||
'items': items,
|
||||
'email': order.email,
|
||||
'payable_with': order.payment_method.lower(),
|
||||
@@ -126,68 +156,71 @@ class Iugu:
|
||||
'cpf_cnpj': order.cnpj if order.cnpj else order.cpf,
|
||||
'address': {
|
||||
'zip_code': order.address.postcode,
|
||||
'street': order.address.street,
|
||||
'number': order.address.street_number,
|
||||
'street': order.address.address1,
|
||||
'number': '',
|
||||
'district': order.address.neighborhood,
|
||||
'city': order.address.city,
|
||||
'state': order.address.state,
|
||||
'complement': order.address.complement,
|
||||
'complement': order.address.address2,
|
||||
'country=': 'Brasil',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=payload, timeout=15)
|
||||
response.raise_for_status()
|
||||
r = requests.post(url, json=payload, timeout=15)
|
||||
r.raise_for_status()
|
||||
except requests.HTTPError as err:
|
||||
logger.exception(err)
|
||||
raise
|
||||
else:
|
||||
response = response.json()
|
||||
pix = (
|
||||
Pix(**response['pix'])
|
||||
if order.payment_method == PaymentMethod.PIX
|
||||
else None
|
||||
)
|
||||
bank_slip = (
|
||||
BankSlip(**response['bank_slip'])
|
||||
if order.payment_method == PaymentMethod.BANK_SLIP
|
||||
else None
|
||||
)
|
||||
return r.json()
|
||||
# pix = (
|
||||
# Pix(**response['pix'])
|
||||
# if order.payment_method == PaymentMethod.PIX
|
||||
# else None
|
||||
# )
|
||||
# bank_slip = (
|
||||
# BankSlip(**response['bank_slip'])
|
||||
# if order.payment_method == PaymentMethod.BANK_SLIP
|
||||
# else None
|
||||
# )
|
||||
|
||||
return Invoice(
|
||||
id=response['secure_id'],
|
||||
pdf=bank_slip.bank_slip_url
|
||||
if bank_slip
|
||||
else '%s.pdf' % response['secure_url'],
|
||||
pix=pix,
|
||||
)
|
||||
# return Invoice(
|
||||
# id=response['secure_id'],
|
||||
# pdf=bank_slip.bank_slip_url
|
||||
# if bank_slip
|
||||
# else '%s.pdf' % response['secure_url'],
|
||||
# pix=pix,
|
||||
# )
|
||||
|
||||
def payment_token(self, credit_card: CreditCard) -> Token:
|
||||
url = self.url(path='/v1/payment_token')
|
||||
payload = {
|
||||
'test': self.credentials.test_mode,
|
||||
'account_id': self.credentials.account_id,
|
||||
'method': 'credit_card',
|
||||
'data': {
|
||||
'number': credit_card.number,
|
||||
'verification_value': credit_card.cvv,
|
||||
'first_name': credit_card.first_name,
|
||||
'last_name': credit_card.last_name,
|
||||
'month': credit_card.exp.strftime('%m'),
|
||||
'year': credit_card.exp.strftime('%Y'),
|
||||
},
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=payload, timeout=15)
|
||||
response.raise_for_status()
|
||||
r = requests.post(
|
||||
url,
|
||||
json={
|
||||
'test': self.credentials.test_mode,
|
||||
'account_id': self.credentials.account_id,
|
||||
'method': 'credit_card',
|
||||
'data': {
|
||||
'number': credit_card.number,
|
||||
'verification_value': credit_card.cvv,
|
||||
'first_name': credit_card.first_name,
|
||||
'last_name': credit_card.last_name,
|
||||
'month': credit_card.exp_month,
|
||||
'year': credit_card.exp_year,
|
||||
},
|
||||
},
|
||||
timeout=15,
|
||||
)
|
||||
r.raise_for_status()
|
||||
except requests.HTTPError as err:
|
||||
logger.exception(err)
|
||||
raise
|
||||
else:
|
||||
return Token(response.json()['id'])
|
||||
return Token(r.json()['id'])
|
||||
|
||||
def charge(
|
||||
self,
|
||||
@@ -234,13 +267,13 @@ class Iugu:
|
||||
url = self.url(path=f'/v1/invoices/{format_id(invoice_id)}')
|
||||
|
||||
try:
|
||||
response = requests.get(url, timeout=15)
|
||||
response.raise_for_status()
|
||||
r = requests.get(url, timeout=15)
|
||||
r.raise_for_status()
|
||||
except requests.HTTPError as err:
|
||||
logger.exception(err)
|
||||
raise
|
||||
else:
|
||||
return response.json()
|
||||
return r.json()
|
||||
|
||||
|
||||
def format_id(invoice_id: str) -> str:
|
||||
|
||||
@@ -39,6 +39,7 @@ Globals:
|
||||
ENROLLMENT_TABLE: !Ref EnrollmentTable
|
||||
COURSE_TABLE: !Ref CourseTable
|
||||
BUCKET_NAME: !Ref BucketName
|
||||
IUGU_API_TOKEN: '{{resolve:ssm:/saladeaula/iugu_api_token}}'
|
||||
|
||||
Resources:
|
||||
EventLog:
|
||||
@@ -46,6 +47,37 @@ Resources:
|
||||
Properties:
|
||||
RetentionInDays: 90
|
||||
|
||||
HttpLog:
|
||||
Type: AWS::Logs::LogGroup
|
||||
Properties:
|
||||
RetentionInDays: 90
|
||||
|
||||
HttpApi:
|
||||
Type: AWS::Serverless::HttpApi
|
||||
Properties:
|
||||
CorsConfiguration:
|
||||
AllowOrigins: ['*']
|
||||
AllowMethods: [POST, OPTIONS]
|
||||
AllowHeaders: [Content-Type, X-Requested-With]
|
||||
|
||||
HttpApiFunction:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
Handler: app.lambda_handler
|
||||
Timeout: 12
|
||||
LoggingConfig:
|
||||
LogGroup: !Ref HttpLog
|
||||
Policies:
|
||||
- DynamoDBWritePolicy:
|
||||
TableName: !Ref OrderTable
|
||||
Events:
|
||||
Post:
|
||||
Type: HttpApi
|
||||
Properties:
|
||||
Path: /
|
||||
Method: POST
|
||||
ApiId: !Ref HttpApi
|
||||
|
||||
EventBillingAppendEnrollmentFunction:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
@@ -286,3 +318,13 @@ Resources:
|
||||
new_image:
|
||||
sk: [generated_items]
|
||||
status: [SUCCESS]
|
||||
|
||||
Outputs:
|
||||
HttpApiUrl:
|
||||
Description: URL of your API endpoint
|
||||
Value:
|
||||
Fn::Sub: 'https://${HttpApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}'
|
||||
HttpApiId:
|
||||
Description: Api ID of HttpApi
|
||||
Value:
|
||||
Ref: HttpApi
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
// Orders
|
||||
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "0", "total": 398, "status": "PENDING", "payment_method": "MANUAL", "tenant_id": "cJtK9SsnJhKPyxESe7g3DG"}
|
||||
{"id": "18f934d8-035a-4ebc-9f8b-6c84782b8c73", "sk": "0", "payment_method": "PAID"}
|
||||
{"id": "6a60d026-d383-4707-b093-b6eddea1a24e", "sk": "items", "items": [{"id": "a810dd22-56c0-4d9b-8cd2-7e2ee9c45839", "name": "pytest", "quantity": 1, "unit_price": 109}]}
|
||||
{"id": "6a60d026-d383-4707-b093-b6eddea1a24e", "sk": "ITEMS", "items": [{"id": "a810dd22-56c0-4d9b-8cd2-7e2ee9c45839", "name": "pytest", "quantity": 1, "unit_price": 109}]}
|
||||
{"id": "a810dd22-56c0-4d9b-8cd2-7e2ee9c45839", "sk": "metadata#betaeducacao", "course_id": "dc1a0428-47bf-4db1-a5da-24be49c9fda6", "create_date": "2025-06-05T12:13:54.371416+00:00"}
|
||||
|
||||
{"id": "2849f1d5-f4f1-411e-8497-ec3a40afc0ab", "sk": "0" "payment_method": "BANK_SLIP", "status": "PENDING", "total": 178.2, "due_date": "", "email": "org+15608435000190@users.noreply.saladeaula.digital", "name": "Beta Educação", "coupon": "10OFF", "discount": -19.8, "create_date": "2026-01-07T19:09:54.193859-03:00", "updated_at": "2026-01-07T19:09:54.871374-03:00", "org_id": "cJtK9SsnJhKPyxESe7g3DG", "subtotal": 198, "tenant_id": "cJtK9SsnJhKPyxESe7g3DG", "cnpj": "15608435000190"}
|
||||
{"id": "2849f1d5-f4f1-411e-8497-ec3a40afc0ab", "sk": "ITEMS", "items": [ { "name": "CIPA Grau de Risco 1", "id": "3c27ea9c-9464-46a1-9717-8c1441793186", "quantity": 1, "unit_price": 99 }, { "name": "CIPA Grau de Risco 2", "id": "99bb3b60-4ded-4a8e-937c-ba2d78ec6454", "quantity": 1, "unit_price": 99 } ], "created_at": "2026-01-07T19:09:54.193859-03:00"}
|
||||
{"id": "2849f1d5-f4f1-411e-8497-ec3a40afc0ab", "sk": "ADDRESS", "city": "São José", "postcode": "88101001", "state": "SC", "created_at": "2026-01-07T19:09:54.193859-03:00", "address1": "Avenida Presidente Kennedy" "address2": "", "neighborhood": "Campinas"}
|
||||
|
||||
// User data
|
||||
{"id": "5OxmMjL-ujoR5IMGegQz", "sk": "0", "name": "Sérgio R Siqueira"}
|
||||
{"id": "cnpj", "sk": "15608435000190", "user_id": "cJtK9SsnJhKPyxESe7g3DG"}
|
||||
|
||||
2
orders-events/uv.lock
generated
2
orders-events/uv.lock
generated
@@ -651,7 +651,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "layercake"
|
||||
version = "0.11.3"
|
||||
version = "0.12.0"
|
||||
source = { directory = "../layercake" }
|
||||
dependencies = [
|
||||
{ name = "arnparse" },
|
||||
|
||||
Reference in New Issue
Block a user