Paddle SDK service

We abstract the Paddle SDK inside an effect service.

@paddle/paddle-node-sdk provides a Paddle class that exposes the Paddle API. Creating a Paddle instance requires a valid Paddle API key.

We export Paddle as a service that uses Config.redacted to provide the API key:

import * as _Paddle from "@paddle/paddle-node-sdk";
import { Config, Effect, Layer, Redacted } from "effect";

export class PaddleSdk extends Effect.Service<PaddleSdk>()("PaddleSdk", {
  effect: Effect.gen(function* () {
    const apiKey = yield* Config.redacted("PADDLE_API_KEY");
    return new _Paddle.Paddle(Redacted.value(apiKey), {
      environment: _Paddle.Environment.sandbox,
      logLevel: _Paddle.LogLevel.verbose,
    });
  }),
}) {}

Wrapping @paddle/paddle-node-sdk inside a service allows to abstract the Paddle SDK.

We can later create a Test layer that allows to mock the Paddle SDK in tests.

On top of PaddleSdk we create a Paddle service that implements an effect wrapper around paddle.webhooks.unmarshal (webhook signature verification):

export class Paddle extends Effect.Service<Paddle>()("Paddle", {
  effect: Effect.gen(function* () {
    const paddle = yield* PaddleSdk;

    const webhooksUnmarshal = ({
      paddleSignature,
      payload,
      webhookSecret,
    }: {
      payload: string;
      webhookSecret: Redacted.Redacted;
      paddleSignature: string;
    }) =>
      Effect.fromNullable(
        paddle.webhooks.unmarshal(
          payload,
          Redacted.value(webhookSecret),
          paddleSignature
        )
      ).pipe(Effect.mapError((cause) => new ErrorPaddle({ cause })));

    return { webhooksUnmarshal };
  }),
}) {}