npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@spherelabs/lite-sdk

v1.3.12

Published

Lite Typescript SDK for Sphere

Downloads

869

Readme

Sphere Protocol

This repository contains the open source code for Sphere's Lite Typescript SDK.

Documentation

Overview

This SDK enables you to create one-time payments for unit/credit based products, with the option of adding different pricing tiers (e.g., Basic, Developer, Enterprise), and the ability to pay in any currency.

There are additional utility functions to fetch quotes and create webhooks.

We also support both 'volume' and 'graduated' billing methods, if necessary. This blog post explains what these mean.

Installation

npm i @spherelabs/lite-sdk

Integration

At a high-level, developers will:

  1. Create a product to describe the goods and services they are selling: including the cost, billing method, and pricing tiers if relevant.
  2. Create a button on your frontend to allow your customer to purchase your products.
  3. Create webhooks to receive payment events for your products.

Integration Guide

We will demonstrate the step-by-step flow for embedding the payments logic into a button.

1. Setup

Create a script with the following:

import { Sphere } from '@spherelabs/lite-sdk';

const sphere = new Sphere({
      env: 'mainnet',
      rpcUrl: 'https://api.mainnet-beta.solana.com',
      signer: '3iJjsyYEDDhkvTNsnwJcVapXEv82Qqbh5BSCjZcamwwFLC9Gdw5rurQxtomLCEjwGHbWYk1p7yoihbQQGXeXsk1p',
});

var product = await sphere.product.create({
      name: 'Service Credits',
      description: 'A product representing credits for your service',
      meta: {},
      images: ['http://localhost:8080/img1.png'],
      currency: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
      taxRate: 0.01,
      billingMode: 'graduated',
      tiers: [
            {
                  units: 1,
                  price: 0.5,
            },
            {
                  units: 3,
                  price: 0.25,
            },
            {
                  units: 5,
                  price: 0.1,
            },
      ],
});
console.log(product.id);

Keep a note of your product's id. You will need to put this into your frontend.

2. Frontend

In your React App, create the following "product purchase" Button:

import { useEffect, useState } from 'react';
import { Sphere } from '@spherelabs/lite-sdk';
import { useWallet } from '@solana/wallet-adapter-react';

export const Button = ({ units, id }: any) => {
      const reactWallet = useWallet();
      const [sphere, setSphere] = useState<Sphere>();
      const [txSig, setTxSig] = useState<string>('');

      useEffect(() => {
            (async () => {
                  if (reactWallet && reactWallet.connected) {
                        try {
                              const _sphere = new Sphere({
                                    env: 'mainnet',
                                    rpcUrl: 'https://solana-mainnet.g.alchemy.com/v2/UesFjy66kB4g4BEa1-JFNCEYIP_FxXvF',
                                    reactWallet,
                              });
                              setSphere(_sphere);
                              await _sphere.prepareFrontend();
                        } catch (error) {
                              console.log(error);
                        }
                  }
            })();
      }, [reactWallet.connected]);

      const onClick = async () => {
            try {
                  console.log(sphere);
                  const _txSig = (await sphere?.product.purchase({
                        id,
                        units,
                  })) as string;
                  setTxSig(_txSig);
            } catch (error) {
                  console.log(error);
            }
      };

      return (
            <>
                  {txSig ? (
                        <p>
                              transactionSignature: &nbsp;
                              <a
                                    href={`https://solscan.io/tx/${txSig}`}
                                    style={{
                                          color: 'blue',
                                          textDecoration: 'underline',
                                    }}
                              >
                                    {txSig}
                              </a>
                        </p>
                  ) : (
                        <button className="buttonPurchase" onClick={onClick}>
                              Purchase Product
                        </button>
                  )}
            </>
      );
};

Now, you can put your button somewhere in your application:

<Button id="product-4d081945-7dab-4b15-8cb2-632ae5818a2f" units={2} />

Users can click this button to purchase the product at a specified amount of units.

We have included a full NextJS React App to demonstrate this.

3. Events

Sphere generates and stores purchase events every time your customers purchases a product.

Event Payloads

The following is an example event body payload for product purchases with SPL tokens:

{
      "id": "event-a5c415c5-1d90-4dfe-99ee-c81538d61d2c", 
      "product": "product-955d6030-f3e3-4aa1-a2cd-87eaab91a7c0", 
      "transferType": "spl", 
      "customer": "6VxGFK7NTv8tmRS2NvMNGAdqwkN5Q1b4X2qEk71Uf8N2", 
      "merchant": "AEHY6aV66YoDMUbhnuXGPANyikooaQgm8spFAJro9AFV", 
      "customerTokenAccount": "AxFuniPo7RaDgPH6Gizf4GZmLQFc4M5ipckeeZfkrPNn", 
      "merchantTokenAccount": "34NsPzjUUMcnG8KAsuPhg6EqeLkmYTpJhpbUb9SRaBiJ", 
      "currency": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", 
      "amount": 10, 
      "fee": 0.03, 
      "units": 10, 
      "fromCurrency": "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R",
      "txSig": "3ozMCwVProD2iksCAMNnfzdN5jGveEYfGjzKZw3UQmZQFPgyt39w2FuN2PPvtgbV2bzpanzss4zonPjKAbdy8etk",
      "created": "2023-02-08T02:51:54.405Z"
}

The following is an example event body payload for product purchases with native SOL:

{
      "id": "event-a5c415c5-1d90-4dfe-99ee-c81538d61d2c", 
      "product": "product-955d6030-f3e3-4aa1-a2cd-87eaab91a7c0", 
      "transferType": "sol", 
      "customer": "6VxGFK7NTv8tmRS2NvMNGAdqwkN5Q1b4X2qEk71Uf8N2", 
      "merchant": "AEHY6aV66YoDMUbhnuXGPANyikooaQgm8spFAJro9AFV", 
      "customerTokenAccount": "", 
      "merchantTokenAccount": "", 
      "currency": "So11111111111111111111111111111111111111111", 
      "amount": 10, 
      "fee": 0.03, 
      "units": 10, 
      "fromCurrency": "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R",
      "txSig": "3ozMCwVProD2iksCAMNnfzdN5jGveEYfGjzKZw3UQmZQFPgyt39w2FuN2PPvtgbV2bzpanzss4zonPjKAbdy8etk",
      "created": "2023-02-08T02:51:54.405Z"
}

Retrieving Events

We have included several functions to retrieve your events.

To get all ids for your events, execute:

const ids: string[] = await sphere.event.getIds()

To get all events associated to a given customer's publickey:

const events = await sphere.event.listByCustomer("OPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")

To get all events associated to your infrastructure:

const events = await sphere.event.list()

4. Webhooks

To propagate purchase events to your API, you may use webhooks. To create a webhook execute the following:

var webhook = await sphere.webhook.create({
      name: 'Example Product Payments Webhook',
      description: 'Ingests product payment events',
      topics: ['sphere.events.product.purchase'],
      url: 'http://localhost:3000',
});

We support events of a single topic: sphere.events.product.purchase. These events are emitted on purchase of a product by one of your customers.

Events will be sent to your API at most 30 seconds after they have been confirmed onchain. If your API is unavaliable Sphere will attempt to deliver events 10 times with 1 second backoff period.

You may only create one webhook per signer. That is to say that, each merchant has at most 1 associated webhook.

Signing Secrets

Webhooks contain a signing secret which you may use to authenticate requests.

const signingSecret: string = webhook.secret;

POST requests made to your API will contain this secret in the Authorization field of the request headers.

You may want to alter or disable your webhook when not in-use. To update your webhook:

var webhook = await sphere.webhook.update({
      name: 'Example Product Payments Webhook',
      description: 'Ingests product payment events',
      topics: ['sphere.events.product.purchase'],
      active: false,
      url: 'http://localhost:3000',
});

5. Development

To test your webhooks locally we recommend using a reverse proxy such as ngrok to create a secure tunnel between a publically exposed endpoint and your API.

Swaps

We support Token Swaps via Jupiter for product purchases. Hence, your customers can pay in the currency of their choice.

To include a swap in a product purchase, select the fromCurrency that the customer is paying in, which they can specify on your frontend. We should note that the merchant will receive the currency originally specified on creation of the product.

const txSig = await sphere.product.purchase({
      id: product.id,
      units: 2,
      swap: {
            fromCurrency: '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
      },
});

When using the swap feature, you may wish to display price quotes to your users given the price structure of your product (e.g., the number of units they want to buy and the currency they wish to pay in). To get a quote in the fromCurrency for a given amount of units:

const quote = await sphere.product.quote({
      id: product.id,
      fromCurrency: '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
      units: 5,
});

Billing Modes

Graduated

Consider the following graduated tier pricing structure: Units [0-100] have a price per unit of 1 Units [101-250] have a price per unit of 2 Units [251+] have a price per unit of 3

So if the customer uses 300 units, the total will be: 100(1) + 150(2) + 50(3) = 550.

This tier structure will have the following array encoding:

const tiers = [
      {
            price: 1,
            units: 100,
      },
      {
            price: 2,
            units: 150,
      },
      {
            price: 3,
            units: 250,
      },
];

Volume

Consider the following volume tiered pricing structure: Units [0-100] have a price per unit of 1 Units [101-250] have a price per unit of 2 Units [251+] have a price per unit of 3

So if the customer uses 300 units, the total will be: 3(300) = 900. Alternatively, if they use 50 units, the total will be 50(1) = 50.

This tier structure will have the following array encoding:

const tiers = [
      {
            price: 1,
            units: 0,
      },
      {
            price: 2,
            units: 100,
      },
      {
            price: 3,
            units: 250,
      },
];

Notice that for volume based billing, the units specified indicate the point at which the given price kicks in (e.g., after the first 100 units, the price becomes 2 instead of 1). This is unlike graduated billing, where the units specify the amount of units that can be consumed at a given price point (e.g., the first 100 units are allocated the price 1, and the next 150 are allocated price 2). In other words, graduated billing defines units by an upper limit per tier, whereas volume based billing defines units by a lower bound per tier. The reason for this difference is because in volume based billing, the pricing is set by the most recent tier. Hence, without using a lower limit, it would be unclear how many units to specify for the final tier.

To illustrate, if volume based billing used the same upper limit method as graduated:

const tiers = [
  {
      price: 1,
      units: 100
  },
  {
      price: 2,
      units: 250
  },
  {
      price: 3,
      units: ?
  },
]

Hence, the last tier's units would be undefined. This is why we use a lower limit for this billing method.

Fees

Sphere charges a fee of 0.3% for all transfers using our infrastructure.

License

Sphere Protocol is licensed under MIT LICENSE.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Sphere SDK by you, as defined in the MIT license, shall be licensed as above, without any additional terms or conditions.