Getting Started with Daisy Widget

Daisy Widget is a library of React components that makes interacting with the full lifecycle of payments and subscriptions convenient and straightforward. It is designed to be used within your project's existing frontend. This guide will go through creating a subscription signup flow, token approval flow, and cancellation flow.

Placeholder for ApproveInput, ApproveButton, SubscribeButton, and StepIndicator

Daisy Widget is just one of three possible ways for your users to pay with Daisy. If you're looking for even more flexibility, you can build everything you need directly with Daisy SDK. If you're looking for an almost-no-code solution, check out Invitations.


Installation and Set Up

Begin by adding the @daisypayments/daisy-widget package to your project. You must also add web3@=1.0.0-beta.37 for the dependency react-metamask to work (the specific version is important here).

yarn add @daisypayments/daisy-widget web3@=1.0.0-beta.37

Next, Daisy Widget components require the use of two React Contexts, MetaMaskContext and DaisyContext. We recommend wrapping your entire app with their respective Context Providers (in the order shown below):

// Root of your React app

import { MetaMaskContext, DaisyContext } from "@daisypayments/daisy-widget";
<MetaMaskContext.Provider immediate>
  <DaisyContext.Provider
    value={{ identifier: "acme-co-subscription-service-id" }}
  >
    <YourApp />
  </DaisyContext.Provider>
</MetaMaskContext.Provider>

We recommend that you pass MetaMaskContext.Provider the immediate prop, otherwise you are responsible for calling react-metamask's openMetaMask() function to authorize web3 access where needed. DaisyContext.Provider requires an object for the value prop, with the identifier key set to your subscription service's globally unique id.

Where to find your subscription service's id in the dashboard

With that done, whenever you would like a component to access MetaMask utilities or the Daisy object, you can provide them as props by simply wrapping the component with the Higher-Order Components, withMetaMask() and withDaisy(), in the order shown below:

const EnhancedSubscriptionPage = withMetaMask(withDaisy(SubscriptionPage));

Signing Up for a Subscription

Signing up for a subscription requires the ApproveButton and SubscribeButton components, and the optional, but recommended ApproveInput, StepIndicator, and Toast components.

Placeholder for ApproveInput boxed off as optional, ApproveButton and SubscribeButton boxed off as required, and StepIndicator boxed off as optional

Two buttons are needed because signing up is a two step process.

First, the user must make a transaction approving the subscription contract to transfer tokens from their wallet.

Second, the user must sign the details of the subscription agreement using MetaMask.


Approval Step

Instantiate ApproveButton, passing the name of an existing plan within the subscription service as plan.

// Signup page of your React app

import { ApproveButton } from "@daisypayments/daisy-widget";
<ApproveButton plan="anvilMysteryBoxMonthly" />

Clicking the button will open MetaMask and prompt the user to approve the number of tokens that the subscription contract is allowed to transfer from their wallet. The number of tokens to approve can be defined in one of three ways.

The first, recommended way is to use Daisy Widget's ApproveInput component. ApproveInput provides the user an intuitive way of defining the approval amount in terms of either tokens or billing periods. The component accepts an optional periods prop to define the initial number of billing currentlyAllowedPeriods; its default value is 12.

Placeholder for ApproveInput
import { ApproveInput } from "@daisypayments/daisy-widget";
<ApproveInput plan="anvilMysteryBoxMonthly" />

Second, if ApproveInput is not used, ApproveButton accepts an approvalAmount prop which sets the number of tokens to approve.

import { ApproveButton } from "@daisypayments/daisy-widget";
<ApproveButton 
  plan="anvilMysteryBoxMonthly" 
  approvalAmount="200" 
/>

Finally, if neither ApproveInput is used nor the approvalAmount prop defined, the number of tokens defaults to the amount needed to pay for 12 billing periods. If more than one of these three ways is provided, Daisy Widget assigns priority in the order they are listed above (e.g. the value from ApproveInput would overrule the approvalAmount prop).

If you would like to conditionally render any part of this flow based on the user's current allowance of approved tokens, use Daisy SDK's allowance() function.

const token = daisy.loadToken();
const currentAllowance = await daisy
  .prepareToken(token)
  .allowance({ tokenOwner: account });

Sign and Submit Step

Next, instantiate SubscribeButton and pass it the same plan name.

// Signup page of your React app

import { SubscribeButton } from "@daisypayments/daisy-widget";
<SubscribeButton plan="anvilMysteryBoxMonthly" />

Clicking the button will open MetaMask and prompt the user to sign the details of the subscription agreement. Once signed, the agreement is sent to Daisy to be executed on the Ethereum blockchain by one of two ways.

Backend Way: The first, recommended way is to pass a handleSignedAgreement prop to SubscribeButton. This handler is responsible for sending the signed agreement to your backend where it needs to be submitted with submit(), and then returning the server response.

const signedAgreementHandler = async submitArgs => {

  try {
    // Send signed agreement to backend which calls daisy.submit(), 
    // stores the returned daisyId, and then returns the response
    const response = await fetch("/submit_agreement/", {
      method: "POST",
      body: JSON.stringify(submitArgs),
      headers:{
        "Content-Type": "application/json"
      }
    })
    return response;
  } catch (error) {
    ...
  }
}
<SubscribeButton
  plan="anvilMysteryBoxMonthly"
  handleSignedAgreement={signedAgreementHandler}
/>

Frontend Way: If handleSignedAgreement is omitted, the agreement is submitted with submit() immediately and SubscribeButton's handleSubscriptionCreation prop is called with the server response.

Whether the agreement is submitted from your backend or frontend, it is very important to associate the returned daisyId with the user at this step!

We recommend the Backend Way as it avoids any risk of daisyId being lost when sent from the user's browser to your backend. But, if you choose not to use handleSignedAgreement and do the agreement submission entirely from the frontend, be sure to associate the response's daisyId with the user in handleSubscriptionCreation!

// NOTE: Previous example preferred to this example!
const subscriptionHandler = async subscription => {

  try {
    // Store user updated with daisyId on backend
    const updatedUser = await fetch("/update_user/", {
      method: "PATCH",
      body: JSON.stringify({
        ...this.props.user, 
        daisyId: subscription.daisyId
      }),
      headers:{
        "Content-Type": "application/json"
      }
    })
    ...
  } catch (error) {
    ...
  }
}
<SubscribeButton
  plan="anvilMysteryBoxMonthly"
  handleSubscriptionCreation={subscriptionHandler}
/>

And that's it! Your page is now ready to accept subscribers!
From here you will likely want to provide a way for existing subscribers to approve more tokens, view their subscription details, and cancel their subscription.


Approving More Tokens for an Existing Subscription

Placeholder for ApproveInput and ApproveButton

Every billing period, the price of the plan will be subtracted from the amount of tokens the user has approved. Once all approved tokens have been spent, the user will need to approve more to continue the subscription. Luckily, doing so is easy. Simply display the same ApproveInput and ApproveButton components to subscribed users somewhere within your app. We recommend still including the Toast component on this page unless you want to be in charge of error handling, but SubscribeButton and StepIndicator should be left out.

// Subscriber account page of your React app

import { ApproveButton, ApproveInput, Toast } from "@daisypayments/daisy-widget";
<ApproveInput plan="anvilMysteryBoxMonthly" />
<ApproveButton plan="anvilMysteryBoxMonthly" />
<Toast />

Cancelling a Subscription

Occasionally a user will need to cancel their subscription. Just as there are ApproveButton and SubscribeButton components, there is a CancelButton as well. Unlike the previous two buttons, CancelButton requires the subscription object as a prop in addition to the plan name. The subscription object can be obtained from getSubscription().

// Subscriber account page of your React app

import { CancelButton } from "@daisypayments/daisy-widget";

const subscription = await daisy.getSubscription(this.props.user.daisyId);
<CancelButton
  plan="anvilMysteryBoxMonthly"
  subscription={subscription}
/>
<Toast />

Clicking the button will open MetaMask and prompt the user to sign the details of the cancellation. Similar to SubscribeButton, the signed cancellation is sent to Daisy by one of two ways. Neither way is strongly preferred over the other as there is no fear of data loss, so feel free to use whichever suits your needs best.

Backend Way: If you would like to handle submitting the signed cancellation with submitCancel() from your backend, a handler is exposed for you to do so, handleSignedCancellation. Once again, this handler is responsible for returning the server response.

const handleCancel = async submitArgs => {

  try {
    // Send signed cancellation to backend, which calls 
    // daisy.submitCancel(), and then returns the response
    const response = await fetch("/submit_cancel/", {
      method: "POST",
      body: JSON.stringify(submitArgs),
      headers:{
        "Content-Type": "application/json"
      }
    })
    return response;
  } catch (error) {
    ...
  }
}
<CancelButton
  plan="anvilMysteryBoxMonthly"
  subscription={subscription}
  handleSignedCancellation={handleCancel}
/>

Frontend Way: If handleSignedCancellation is omitted, the agreement is submitted with submitCancel() immediately

In both the Backend Way and the Frontend Way, if the handleSubscriptionCancellation prop is passed to CancelButton, it is called with the server response.


What is Required of the User?

A user on your site is interested in subscribing to your service. There are some prerequisites to signing up for non-fiat denominated subscriptions. What do they need? The user must:

  • Have MetaMask installed or be using a dapp browser (for approving tokens and signing the subscription agreement).
  • Have enough of one of the subscription's accepted ERC20 tokens to pay for at least one billing period.
  • Have enough ETH to pay the gas fee of the approval step (almost always less than $0.10 USD).

Luckily, if any of these requirements are not met, Daisy Widget can do the work of surfacing actionable error messages to the user for you with the optional Toast component.


Still have questions after reading this guide? Feel free to contact us at hello@daisypayments.com.