Como resolvemos o problema de moeda de mercado do Recharge com AWS Lambda — Blog AM Lab
Assinaturas · AWS 14 abr 2026 · 12 min de leitura

Como resolvemos o problema de moeda de mercado do Recharge com AWS Lambda

Recharge, Shopify e AWS Lambda: correção de moeda de mercado em assinaturas
Assinaturas fora do mercado base ficavam com preço e moeda divergentes após qualquer update.


Background

O Recharge não recalcula automaticamente a presentment_currency nem o preço associado quando uma assinatura é atualizada — troca de variante, nova assinatura, qualquer alteração. Para clientes fora do mercado base em USD, isso significa preço e moeda errados armazenados indefinidamente.

A solução foi uma etapa pós-atualização dentro do nosso serviço em AWS Lambda: ela detecta mercados fora do USD e corrige o preço programaticamente, buscando o valor contextual correto direto no Shopify.

⚡ Contexto técnico: este artigo assume familiaridade com Recharge Subscriptions API, Shopify Admin GraphQL, AWS Lambda e Serverless Framework. Os exemplos estão em TypeScript sobre Node.js 20.

01Visão geral da arquitetura

O serviço é uma função AWS Lambda publicada com Serverless Framework v3, rodando em Node.js 20.x / ARM64 dentro de uma VPC privada (usrv-subscriptions). Expõe um único endpoint HTTP via API Gateway:

POST /{stage}/subscriptions/update

Toda configuração sensível — tokens de API, valor do mercado comparador, URLs da Shopify — fica no AWS SSM Parameter Store e é injetada como variável de ambiente da Lambda no momento do deploy.

MARKET_TO_COMPARE: ${ssm:/${self:provider.stage}/GLOBAL/MARKET_TO_COMPARE}

Esse valor é tipicamente USD — o mercado base que o Recharge assume por padrão.

02Request Flow

Handler e middleware

As requisições chegam em src/handler/subscriptionsUpdate.ts e passam por um pipeline de middlewares Middy:

httpJsonBodyParserFaz o parse do body JSON bruto
validatorValida o schema: subsToUpdate e subsToCancel obrigatórios, subsToCreate opcional
httpCors · logger · httpErrorHandlerCORS, log estruturado e tratamento centralizado de erros

O handler instancia três clientes de serviço — RechargeService, ShopifyGateway e DynamoService — e delega a execução para SubscriptionService.subscriptionsUpdate.

Operações em lote no Recharge

Três operações em lote são executadas sequencialmente contra a REST API do Recharge (https://api.rechargeapps.com):

CriarPOST addresses/{addressId}/subscriptions-bulkCria novas assinaturas
CancelarPUT addresses/{addressId}/subscriptions-bulkDefine status: cancelled
AtualizarPUT addresses/{addressId}/subscriptions-bulkVariante, frequência e quantidade

Todas usam uma instância compartilhada do Axios com retry: 3 tentativas, backoff de 1s / 2s / 3s, disparando em erros 5xx e falhas de rede. Ao final, os resultados são persistidos em DynamoDB (usrv-subscriptions-{stage}) com ID da assinatura, timestamp e ação (created, updated, deleted) como log de auditoria.

Detecção  da moeda de mercado

Aqui está o núcleo da correção. Assim que as operações em lote terminam:

const isMarketPriceUpdateRequired =
  await this.isSubscriptionRequiredToUpdateMarketPrice(baseSubId);

private async isSubscriptionRequiredToUpdateMarketPrice(baseSubId: string) {
  const market = await this.rechargeService.getMarket(baseSubId);
  const marketToCompare = `${process.env.MARKET_TO_COMPARE}`;
  return market !== marketToCompare;
}

O fluxo é direto: chama GET /subscriptions/{id}, lê o campo presentment_currency e compara com MARKET_TO_COMPARE. Se forem diferentes, dispara a correção de preço.

Resolução do country code

O país do cliente é resolvido em duas chamadas: GET /subscriptions/{id} extrai o address_id e GET /addresses/{addressId} devolve o country_codeCA, GB, AU, e assim por diante.

Buscando o preço de mercado no Shopify

Para cada assinatura criada ou atualizada, o serviço consulta o Shopify Admin GraphQL usando contextualPricing — o preço calculado pela própria Shopify para aquele mercado:

query getProductVariant($id: ID!, $country: CountryCode!) {
  productVariant(id: $id) {
    id
    contextualPricing(context: { country: $country }) {
      price {
        amount
        currencyCode
      }
    }
  }
}

# variables
# id:      gid://shopify/ProductVariant/{shopify_variant_id}
# country: {country_code}
🔐 Por que a Admin API: a autenticação usa X-Shopify-Access-Token e o preço vem resolvido server-side. A Storefront API (priceV2) exigiria um buyer token — inviável em um job de backend.

Correção do preço no Recharge

Com o preço correto em mãos, um único PUT resolve:

PUT /subscriptions/{subId} com body contendo price: market.price e presentment_currency: market.currency
EndpointPUT /subscriptions/{subId}
Campos atualizadosprice · presentment_currency

O fluxo ponta a ponta

Client → POST /v1/subscriptions/update
         (subsToCreate, subsToCancel, subsToUpdate)
  │
  ▼
AWS Lambda (Handler + Middy)
  │
  ▼
SubscriptionService
  ├─ Recharge: createSubscriptions (lote)
  ├─ Recharge: cancelSubscriptions (lote)
  ├─ Recharge: updateSubscriptions (lote)
  ├─ DynamoDB: batchInsert (auditoria)
  │
  ├─ Recharge GET /subscriptions/{id}
  │    └─ lê presentment_currency
  │         │
  │         ▼ (se moeda ≠ MARKET_TO_COMPARE)
  ├─ Recharge GET /subscriptions/{id} → address_id
  │    └─ Recharge GET /addresses/{id} → country_code
  │
  └─ para cada assinatura criada/atualizada:
       ├─ Shopify Admin GraphQL: contextualPricing(country)
       │    └─ retorna { amount, currencyCode }
       └─ Recharge PUT /subscriptions/{id}
            └─ { price, presentment_currency }

03Decisões de projeto

01Mercado base configurávelMARKET_TO_COMPARE vive no SSM, não hardcoded. Cada stage tem sua configuração e mudar não exige novo deploy.
02Execução condicional — a correção é ignorada quando nenhuma assinatura foi atualizada ou quando a moeda já bate com o mercado base. Zero chamadas desnecessárias.
03Cobre criadas e atualizadasvalidUpdateSubs e validCreateSubs são mesclados, garantindo que toda assinatura relevante seja corrigida.
04Shopify Admin API por escolhacontextualPricing em vez da Storefront API: sem buyer token, sem sessão de comprador.
05Resiliência com retries — até 3 tentativas nas chamadas ao Recharge, absorvendo falhas transitórias (5xx e rede) sem intervenção manual.

Conclusão

Operações de assinatura multi-mercado expõem lacunas que nenhuma plataforma resolve sozinha. O Recharge cobre o ciclo de vida da recorrência; a Shopify cobre a precificação por mercado. Costurar as duas de forma confiável é trabalho de engenharia.

A lição prática: trate a correção de moeda como uma etapa explícita e idempotente do fluxo, não como efeito colateral. Com detecção condicional, retries e log de auditoria em DynamoDB, o custo operacional é próximo de zero e o cliente nunca mais vê o preço errado.

[ ASSINATURAS · MULTI-MERCADO ]
Sua operação de recorrência cobra na moeda errada?

Auditamos o fluxo de assinaturas, mapeamos onde preço e moeda divergem e implementamos a correção.

Falar com a engenharia ↗
Leia também Ver todos os artigos →