{"_id":"@anastasia-labs/payment-subscription-off-chain","name":"@anastasia-labs/payment-subscription-off-chain","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@anastasia-labs/payment-subscription-off-chain","version":"1.0.0","description":"Off-Chain SDK for Payment Subscription Smart Contract","main":"./dist/index.js","types":"./dist/index.d.ts","type":"module","keywords":[],"author":"","license":"ISC","devDependencies":{"@noble/hashes":"^1.5.0","@sinclair/typebox":"^0.25.24","@types/node":"^20.17.6","@typescript-eslint/eslint-plugin":"^5.62.0","@typescript-eslint/parser":"^5.62.0","eslint":"^8.57.1","eslint-config-prettier":"^8.10.0","ts-node":"^10.9.2","tsup":"^6.7.0","typescript":"^5.6.3","vitest":"0.34.6"},"directories":{"test":"test"},"dependencies":{"@lucid-evolution/lucid":"0.4.16","@noble/hashes":"^1.6.1","dotenv":"^16.4.7","effect":"^3.11.2"},"packageManager":"pnpm@9.7.1+sha512.faf344af2d6ca65c4c5c8c2224ea77a81a5e8859cbc4e06b1511ddce2f0151512431dd19e6aff31f2c6a8f5f2aced9bd2273e1fed7dd4de1868984059d2c4247","scripts":{"test":"export NODE_ENV='emulator' && vitest run","build":"tsup src/index.ts --minify --format esm,cjs --dts --clean","lint":"eslint","repack":"pnpm run build  && pnpm pack","ts-node":"ts-node"},"_id":"@anastasia-labs/payment-subscription-off-chain@1.0.0","_integrity":"sha512-FfZvQkV1JekIbplwgEjSBx1C46Lu5qiYnuldsIzQwU6e5Yo06ui1TcIQFxiu5Y1nawqxU3lyi+dDIxttRzRONA==","_resolved":"/tmp/c8787ab9869a9a5062e27d124a1fb9a9/anastasia-labs-payment-subscription-off-chain-1.0.0.tgz","_from":"file:anastasia-labs-payment-subscription-off-chain-1.0.0.tgz","_nodeVersion":"18.20.5","_npmVersion":"10.8.2","dist":{"integrity":"sha512-FfZvQkV1JekIbplwgEjSBx1C46Lu5qiYnuldsIzQwU6e5Yo06ui1TcIQFxiu5Y1nawqxU3lyi+dDIxttRzRONA==","shasum":"b48e0598642b32b45c27176765e0a5c5be169f08","tarball":"https://registry.npmjs.org/@anastasia-labs/payment-subscription-off-chain/-/payment-subscription-off-chain-1.0.0.tgz","fileCount":5,"unpackedSize":87495,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmf/t/ctsinBJ8zI3Ut7u59uRwRy6PAO6yrBKfZli45gIgAR1zx7S6dxU07UdHhKcAHiGDPgxiC0w7xbm/VNXs2xI="}]},"_npmUser":{"name":"anastasia-labs","email":"info@anastasialabs.com"},"maintainers":[{"name":"anastasia-labs","email":"info@anastasialabs.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/payment-subscription-off-chain_1.0.0_1734694248329_0.5459249909797872"},"_hasShrinkwrap":false}},"time":{"created":"2024-12-20T11:30:48.231Z","1.0.0":"2024-12-20T11:30:48.514Z","modified":"2024-12-20T11:30:48.850Z"},"maintainers":[{"name":"anastasia-labs","email":"info@anastasialabs.com"}],"description":"Off-Chain SDK for Payment Subscription Smart Contract","keywords":[],"license":"ISC","readme":"# Table of Contents\n\n- [Payment Subscription Offchain](#payment-subscription-offchain)\n  - [Introduction](#introduction)\n  - [Documentation](#documentation)\n  - [Usage Example](#usage-example)\n    - [Setup](#setup-lucid--subscription-scripts)\n    - [Create a Service](#create-a-service)\n    - [Create a User Account](#create-a-user-account)\n    - [Initiate a Subscription](#initiate-a-subscription)\n    - [Unsubscribe](#unsubscribe)\n    - [Merchant Withdraw Subscription Fees](#merchant-withdraw-subscription-fees)\n  - [Local Build](#local-build)\n  - [Test Framework](#test-framework)\n  - [Running Tests](#running-tests)\n\n<!-- TODO: Clean up the examples with actual code -->\n# Payment Subscription Offchain\n\n## Introduction\n\nThe Payment Subscription Off-Chain SDK is a TypeScript library that conveniently interfaces with an Aiken-based Payment Subscription Smart Contract on the Cardano blockchain. It provides developers with an easy-to-use interface, enabling users to effortlessly manage recurring payments directly from their wallets. This SDK offers a decentralized and trustless solution for subscription-based services, leveraging the automation capabilities of smart contracts.\n\n**Key features:**\n\n- **Effortless Management of Recurring Payments:** Set up, manage, and cancel recurring payments seamlessly.\n\n- **User-Controlled Subscriptions:** Users maintain control over their funds without the need for intermediaries..\n- **Addition or Removal of Signers:** Update the list of signatories and threshold as needed.\n- **Secure and Transparent:** Built on the Cardano blockchain, ensuring security and transparency.\n- **Flexible Subscription Management:** Supports creation, updating, and cancellation of subscriptions.\n\nThis project is funded by the Cardano Treasury in [Catalyst Fund 11](https://projectcatalyst.io/funds/11/cardano-use-cases-product/anastasia-labs-x-maestro-plug-n-play-20)\n\n## Documentation\n\n### What is a Subscription Payments Smart Contract?\n\nA Subscription Payments Smart Contract is a blockchain-based contract that automates recurring payments between users and service providers without intermediaries. It allows users to authorize scheduled payments directly from their wallets, enhancing security and control over their funds.\n\n### How Does This Project Facilitate Payment Subscription Transactions?\n\nThis project provides an off-chain SDK to interact along with our [Payment Subscription Smart Contract](https://github.com/Anastasia-Labs/payment-subscription). The contract enables:\n\n- **Effortless Recurring Payments:** Automate subscription payments without intermediaries.\n\n- **User-Controlled Subscriptions:** Users have full control over their subscriptions and funds.\n- **Secure Transactions:** Leverages the security and transparency of the Cardano blockchain.\n- **Flexible Subscription Management:** Supports creation, updating, and cancellation of subscriptions.\n\n\n### Design Documentation\n\nFor a comprehensive understanding of the contract's architecture, design decisions, and implementation details, please refer to the [Payment Subscription Design Documentation](https://github.com/Anastasia-Labs/payment-subscription/blob/main/docs/payment-subscription-design-specs/subscription-smart-contract.pdf). This documentation provides in-depth insights into the contract's design, including its components, and detailed explanations of its functionality.\n\n## Usage Example\n\n### Install package\n\n```sh\nnpm install @anastasia-labs/payment-subscription-offchain\n```\n\nor\n\n```sh\npnpm install @anastasia-labs/payment-subscription-offchain\n```\n\n### Setup Lucid & Subscription Scripts\n\n```ts\nimport { Lucid, Maestro } from \"@lucid-evolution/lucid\";\n\nconst lucid = await Lucid(\n  new Maestro({\n    network: \"Preprod\", // For MAINNET: \"Mainnet\"\n    apiKey: \"<Your-API-Key>\", // Get yours by visiting https://docs.gomaestro.org/docs/Getting-started/Sign-up-login\n    turboSubmit: false, // Read about paid turbo transaction submission feature at https://docs.gomaestro.org/docs/Dapp%20Platform/Turbo%20Transaction\n  }),\n  \"Preprod\" // For MAINNET: \"Mainnet\"\n);\n\nlucid.selectWallet.fromPrivateKey(\"your secret key here e.g. ed25519_...\");\n\n// Prepare the validator scripts\nconst serviceScript: SpendingValidator = {\n  type: \"PlutusV2\",\n  script: serviceValidator.compiledCode,\n};\n\nconst accountScript: SpendingValidator = {\n  type: \"PlutusV2\",\n  script: accountValidator.compiledCode,\n};\n\nconst paymentScript: SpendingValidator = {\n  type: \"PlutusV2\",\n  script: paymentValidator.compiledCode,\n};\n\nconst subscriptionScripts = {\n  service: serviceScript.script,\n  account: accountScript.script,\n  payment: paymentScript.script,\n};\n```\n\n### Create a Service\n\n```ts\nimport { createService, CreateServiceConfig } from \"@anastasia-labs/payment-subscription-offchain\";\n\n// Define merchant address\nconst merchantAddress = \"addr_test1...\";\n\n// Configure the service configuration\nconst serviceConfig: CreateServiceConfig = {\n  service_fee: {\n    currencySymbol: '', // For ADA, use empty string\n    tokenName: '',      // For ADA, use empty string\n  },\n  service_fee_qty: 100_000_000n, // 100 ADA in lovelace\n  penalty_fee: {\n    currencySymbol: '', // For ADA, use empty string\n    tokenName: '',      // For ADA, use empty string\n  },\n  penalty_fee_qty: 10_000_000n,  // 10 ADA in lovelace\n  interval_length: 30n * 24n * 60n * 60n * 1000n, // 30 days in milliseconds\n  num_intervals: 12n, // 12 intervals (e.g., months)\n  minimum_ada: 2_000_000n, // Minimum ADA required\n  is_active: true,\n  scripts: {\n    spending: subscriptionScripts.serviceScript.script,\n    minting: '', // Minting script if applicable\n    staking: '', // Staking script if applicable\n  },\n};\n\n// Create the service\nconst createServiceTxUnsigned = await createService(lucid, serviceConfig);\n\nif (createServiceTxUnsigned.type === \"ok\") {\n  // Sign the transaction with the merchant's wallet\n  const createServiceTxSigned = await createServiceTxUnsigned.data.sign().complete();\n  const createServiceTxHash = await createServiceTxSigned.submit();\n  console.log(`Service Created: ${createServiceTxHash}`);\n} else {\n  console.error(\"Failed to create service:\", createServiceTxUnsigned.error);\n}\n\n```\n\n### Create a User Account\n\n```ts\nimport { createAccount, CreateAccountConfig } from \"@anastasia-labs/payment-subscription-offchain\";\n\n// Define user address\nconst userAddress = \"addr_test1...\";\n\n// Configure the account parameters\nconst accountConfig: CreateAccountConfig = {\n  email: 'user@example.com',\n  phone: '+1234567890',\n  account_created: BigInt(Math.floor(Date.now() / 1000)), // Current UNIX timestamp\n  scripts: {\n    spending: subscriptionScripts.accountScript.script,\n    minting: '', // Minting script if applicable\n    staking: '', // Staking script if applicable\n  },\n// Create the user account\nconst createAccountTxUnsigned = await createAccount(lucid, accountConfig);\n\nif (createAccountTxUnsigned.type === \"ok\") {\n  // Sign the transaction with the user's wallet\n  const createAccountTxSigned = await createAccountTxUnsigned.data.sign().complete();\n  const createAccountTxHash = await createAccountTxSigned.submit();\n  console.log(`Account Created: ${createAccountTxHash}`);\n} else {\n  console.error(\"Failed to create account:\", createAccountTxUnsigned.error);\n}\n\n```\n### Initiate a Subscription\n\n```ts\nimport { initiateSubscription, InitiateSubscriptionConfig } from \"@anastasia-labs/payment-subscription-offchain\";\n\n// Configure the subscription parameters\nconst subscriptionConfig: InitPaymentConfig = {\n  service_nft_tn: 'SERVICE_NFT_TOKEN_NAME', // Replace with actual token name\n  account_nft_tn: 'ACCOUNT_NFT_TOKEN_NAME', // Replace with actual token name\n  subscription_fee: {\n    currencySymbol: '', // For ADA, use empty string\n    tokenName: '',      // For ADA, use empty string\n  },\n  total_subscription_fee: 1_200_000_000n, // Total for 12 months (1,200 ADA)\n  subscription_start: BigInt(Math.floor(Date.now() / 1000)),\n  subscription_end:\n    BigInt(Math.floor(Date.now() / 1000)) + 12n * 30n * 24n * 60n * 60n, // 12 months later\n  interval_length: 30n * 24n * 60n * 60n, // 30 days in seconds\n  interval_amount: 100_000_000n, // 100 ADA per interval\n  num_intervals: 12n,\n  last_claimed: BigInt(Math.floor(Date.now() / 1000)),\n  penalty_fee: {\n    currencySymbol: '', // For ADA, use empty string\n    tokenName: '',      // For ADA, use empty string\n  },\n  penalty_fee_qty: 10_000_000n, // 10 ADA\n  minimum_ada: 2_000_000n,\n  service_ref_token: 'SERVICE_REF_TOKEN', // Replace with actual unit\n  account_user_token: 'ACCOUNT_USER_TOKEN', // Replace with actual unit\n  scripts: {\n    spending: subscriptionScripts.paymentScript.script,\n    minting: '', // Minting script if applicable\n    staking: '', // Staking script if applicable\n  },\n};\n\n// Initiate the subscription\nconst initiateSubTxUnsigned = await initiateSubscription(lucid, subscriptionConfig);\n\nif (initiateSubTxUnsigned.type === \"ok\") {\n  // Sign the transaction with the user's wallet\n  const initiateSubTxSigned = await initiateSubTxUnsigned.data.sign().complete();\n  const initiateSubTxHash = await initiateSubTxSigned.submit();\n  console.log(`Subscription Initiated: ${initiateSubTxHash}`);\n} else {\n  console.error(\"Failed to initiate subscription:\", initiateSubTxUnsigned.error);\n}\n\n```\n\n### Unsubscribe\n\n```ts\nimport { unsubscribe, UnsubscribeConfig } from \"@anastasia-labs/payment-subscription-offchain\";\n\n// Configure the unsubscription parameters\nconst unsubscribeConfig: UnsubscribeConfig = {\n  service_nft_tn: 'SERVICE_NFT_TOKEN_NAME', // Replace with actual token name\n  account_nft_tn: 'ACCOUNT_NFT_TOKEN_NAME', // Replace with actual token name\n  currentTime: BigInt(Math.floor(Date.now() / 1000)),\n  user_token: 'USER_TOKEN_UNIT', // Replace with actual unit\n  ref_token: 'REF_TOKEN_UNIT', // Replace with actual unit\n  payment_policy_Id: 'PAYMENT_POLICY_ID', // Replace with actual policy ID\n  payment_scripts: {\n    spending: subscriptionScripts.paymentScript.script,\n    minting: '', // Minting script if applicable\n    staking: '', // Staking script if applicable\n  },\n};\n\n// Unsubscribe from the service\nconst unsubscribeTxUnsigned = await unsubscribe(lucid, unsubscribeConfig);\n\nif (unsubscribeTxUnsigned.type === \"ok\") {\n  // Sign the transaction with the user's wallet\n  const unsubscribeTxSigned = await unsubscribeTxUnsigned.data.sign().complete();\n  const unsubscribeTxHash = await unsubscribeTxSigned.submit();\n  console.log(`Unsubscribed Successfully: ${unsubscribeTxHash}`);\n} else {\n  console.error(\"Failed to unsubscribe:\", unsubscribeTxUnsigned.error);\n}\n```\n\n### Merchant Withdraw Subscription Fees\n\n```ts\nimport { withdrawFees, WithdrawFeesConfig } from \"@anastasia-labs/payment-subscription-offchain\";\n\n// Configure the withdrawal parameters\nconst withdrawConfig: MerchantWithdrawConfig = {\n  last_claimed: previousClaimTimestamp, // As bigint\n  payment_policy_Id: 'PAYMENT_POLICY_ID', // Replace with actual policy ID\n  merchant_token: 'MERCHANT_TOKEN_UNIT', // Replace with actual unit\n  service_ref_token: 'SERVICE_REF_TOKEN', // Replace with actual unit\n  serviceUTxOs: [], // Provide list of UTxOs if necessary\n  scripts: {\n    spending: subscriptionScripts.paymentScript.script,\n    minting: '', // Minting script if applicable\n    staking: '', // Staking script if applicable\n  },\n};\n\n\n// Withdraw subscription fees\nconst withdrawTxUnsigned = await withdrawFees(lucid, withdrawConfig);\n\nif (withdrawTxUnsigned.type === \"ok\") {\n  // Sign the transaction with the merchant's wallet\n  const withdrawTxSigned = await withdrawTxUnsigned.data.sign().complete();\n  const withdrawTxHash = await withdrawTxSigned.submit();\n  console.log(`Fees Withdrawn: ${withdrawTxHash}`);\n} else {\n  console.error(\"Failed to withdraw fees:\", withdrawTxUnsigned.error);\n}\n\n```\n\n## Local Build\n\nIn the main directory\n\n```\npnpm run build\n```\n\n## Test framework\n\nhttps://github.com/vitest-dev/vitest\n\n## Running Tests\n\n```sh\npnpm test\n```\n\n![payment-subscription-offchain](/docs/images/offchain_sdk_tests.gif)\n\nTest results:\n\n![alt text](/docs/images/offchain_tests.png)\n\nEach test case is designed to validate specific aspects of the multi-signature contract,To run only specific tests, do:\n\n```sh\npnpm test test/test-case-function-name.test.ts\n```\n\n","readmeFilename":"README.md"}