Bitcoin Guide

Quickly and easily accept Bitcoin, a digital cryptocurrency, alongside other types of payment. If you need help after reading this, check out our answers to common questions or chat live with other developers in #stripe on freenode.

Stripe users in the United States can accept Bitcoin for USD payments using either Checkout or Stripe.js. Many users can even start accepting Bitcoin payments without needing to make changes to their server-side integration—Stripe’s API supports Bitcoin payments in the same way as credit card payments.

To see how a Bitcoin payment works in Checkout, click the button below and fill in the resulting form with any random, syntactically valid email address.

Payment flow

Stripe handles Bitcoin payments similar to card payments, though with some key differences. When accepting a card payment, the customer provides their card information and your integration then creates a charge. Unless the card issuer declines the payment, the payment process is completed. The card details can also be saved and used repeatedly to create additional charges.

With Bitcoin, the process is inverted. Instead of charging a specified amount, the customer must send the amount in bitcoin from their digital wallet, which is used to complete the payment.

  1. The customer is notified of the amount in USD that needs to be paid. Stripe determines what amount of bitcoin (BTC) is needed to convert to the specified amount in USD.
  2. A source of type bitcoin and flow receiver is created—a wrapper around a Bitcoin address that provides a virtual location that your customer can send (push) the payment to.
  3. Once the correct amount of bitcoin has been pushed into the receiver, a charge can be created using the now chargeable source. This is the same process as using a credit card token to create a charge.

Unlike credit card payments, Bitcoin payments cannot be declined. Once the funds are in the receiver, the charge will succeed as the specified amount has already been sent.

A Bitcoin source can only be used for a single payment—Stripe does not currently support recurring or repeated payments with Bitcoin.

Quickstart using Checkout

The simplest way to accept Bitcoin is with Checkout. After integrating Checkout for card payments, only one code change is needed to begin accepting Bitcoin payments—the addition of data-bitcoin="true" in the form’s code:

<form action="" method="POST">
  <script
    src="https://checkout.stripe.com/checkout.js" class="stripe-button"
    data-key="pk_test_6pRNASCoBOKtIshFeQd4XMUh"
    data-amount="2000"
    data-name="Demo Site"
    data-description="2 widgets ($20.00)"
    data-image="/img/documentation/checkout/marketplace.png"
    data-label="Pay with Card or Bitcoin"
    data-locale="auto"
    data-currency="usd"
    data-bitcoin="true">
  </script>
</form>

After specifying the amount in USD that you want to receive, Stripe handles displaying the converted amount in BTC that your customer needs to pay. Once a Bitcoin payment is received, Checkout submits the form with the following extra fields:

  • stripeToken: The ID of the chargeable source object
  • stripeTokenType: The type of token returned—the value is source_bitcoin
  • stripeEmail: The email address provided by the customer

You then create a charge on your server, using the returned source object’s ID, in the same way you would for a credit card token. The charge request process is explored in more detail in the later section about creating a charge.

Using Stripe.js

You can use Stripe.js if you want complete control over your Bitcoin payment flow, user interface, polling logic, etc. By contrast, Checkout handles all of this for you.

Generating a Bitcoin address for payment

Once you decide how much you’d like to charge, generate a Bitcoin address by creating a source object of type bitcoin. Bitcoin sources have a receiver subhash that includes a Bitcoin address where the customer can send bitcoin as well as the USD amount received so far. They also have a bitcoin subhash that contains the converted BTC amount for the customer to send.

While source objects are treated very similarly to credit card tokens, there is a major difference. When either Checkout or Stripe.js (via callback) passes a source back to the server, that source is “filled” and becomes chargeable—the customer has sent bitcoin to the specified Bitcoin address, and the bitcoin were converted to USD.

Creating and displaying a Source object

A source is first created via the Stripe API with the amount in USD that should be charged. You must also specify the customer’s email address.

# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

source = Stripe::Source.create(
  :type => "bitcoin",
  :amount => 1000,
  :currency => "usd",
  :owner => {
    :email => "payinguser+fill_now@example.com",
  },
)
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

source=stripe.Source.create(
  type='bitcoin',
  amount=1000,
  currency='usd',
  owner={
    "email":'payinguser+fill_now@example.com'
  }
)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

$source = \Stripe\Source::create(array(
  "type" => "bitcoin",
  "amount" => 1000,
  "currency" => "usd",
  "owner" => array(
    "email" => "payinguser+fill_now@example.com"
  )
));
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

Map<String, Object> ownerParams = new HashMap<String, Object>();
ownerParams.put("email", "payinguser+fill_now@example.com");

Map<String, Object> sourceParams = new HashMap<String, Object>();
sourceParams.put("type", "bitcoin");
sourceParams.put("amount", 1000);
sourceParams.put("currency", "usd");
sourceParams.put("owner", ownerParams);

Source source = Source.create(sourceParams);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

stripe.source.create({
  type: "bitcoin",
  amount: 1000,
  currency: "usd",
  owner: {
    email: "payinguser+fill_now@example.com"
  }
}, function(err, source) {
  // asynchronously called
});
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.Key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

sourceParams := &stripe.SourceObjectParams{
  Type: "bitcoin",
  Amount: 1000,
  Currency: "usd",
  Owner: &stripe.SourceOwnerParams{
    Email: "payinguser+fill_now@example.com",
  },
}

s, err := source.New(sourceParams)

if err != nil {
  // handle error
  return;
}

The created source exposes an amount in bitcoin (BTC), and an address that the customer can send their payment to:

{
  "id": "src_18h4EOEniDLYboM3JphgS37m",
  "object": "source",
  "amount": 200,
  "client_secret": "src_client_secret_OkNv1HJ3melzdk2bgd2o4es8",
  "created": 1470864440,
  "currency": "usd",
  "flow": "receiver",
  "livemode": true,
  "metadata": {},
  "owner": {
    "address": null,
    "email": "jenny@rosen.net",
    "name": "Jenny Rosen",
    "phone": null,
    "verified_address": null,
    "verified_email": null,
    "verified_name": null,
    "verified_phone": null
  },
  "receiver": {
    "address": "1Nar8gbhZqahaKdoxLAnxVhGjd5YoHs8T1",
    "amount_charged": 0,
    "amount_received": 0,
    "amount_returned": 0,
    "refund_attributes_method": "email",
    "refund_attributes_status": "missing"
  },
  "status": "pending",
  "type": "bitcoin",
  "usage": "single_use",
  "bitcoin": {
    "address": "1Nar8gbhZqahaKdoxLAnxVhGjd5YoHs8T1",
    "amount": 334400,
    "amount_charged": 0,
    "amount_received": 0,
    "amount_returned": 0,
    "uri": "bitcoin:1Nar8gbhZqahaKdoxLAnxVhGjd5YoHs8T1?amount=0.003344"
  }
}

There are three pieces of information you should display to the customer:

  • bitcoin[amount]: The amount (in Satoshi) that the customer must send. This amount, like all amounts used by the Stripe API, represents the smallest unit of currency. There are 108 (100,000,000) satoshi in one bitcoin, so the returned bitcoin[amount] must be divided by 100,000,000 to present the amount in BTC.
  • receiver[address]: The bitcoin address that is specific to this receiver
  • bitcoin[uri]: An encoding of the amount and address. If you encode this URI as a QR code, many Bitcoin apps can scan it. If this URI is presented as a hyperlink, many customers can click on it to activate their preferred Bitcoin client.

Polling the source

Bitcoin payments are asynchronous: after generating the address, you need to wait for the customer to send the payment. We provide a utility method that will help you poll the Stripe API for changes to the source object:

Stripe.source.poll(
  'src_18h4EOEniDLYboM3JphgS37m',
  'src_client_secret_OkNv1HJ3melzdk2bgd2o4es8',
  function(status, source) {
    // `source`: is the source object.
    // `status`: is the HTTP status. if non 200, an error occured
    //          and the poll is canceled.

    // This handler is called as soon as the source is retrieved and subsequently
    // anytime the source's status (`source.status`) is updated.
});

The method takes in a callback as third argument; when the requested amount is received (when the source is “filled” and its status becomes chargeable), Stripe.js will call the specified handler. You can use this function to post the ID of the source to your server, along with any other necessary customer information.

Because a Bitcoin source’s guaranteed exchange rate expires after 10 minutes, sources of type bitcoin are canceled after this time elapses. Your handler will be called client-side and the source’s status will be canceled. Any fund received after a source is canceled will be automatically refunded to the customer.

When you receive that callback you can instruct your servers to create a new Source, update the payment page with this new receiver’s information and start polling it.

Creating a charge

When the source has become chargeable, it should be submitted to your server to make a charge request and complete the payment, as you would with a credit card token. If you don’t create a charge using the source, you will not be paid the funds from the transaction.

Sources with status chargeable are canceled after one hour if they are not charged. When they get canceled the USD amount of the source is converted back to BTC and returned to the customer (if the USD/BTC rate moved during that hour the amount of BTC returned to the customer might be different from the amount they sent).

The charge request must use the same amount and currency (USD) as the source, and a charge cannot be for less than the source’s amount. Additionally, a source can only be used once, and only if it has been filled and has a status chargeable.

# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

source = Stripe::Source.create(
  :type => "bitcoin",
  :amount => 1000,
  :currency => "usd",
  :owner => {
    :email => "payinguser+fill_now@example.com",
  },
)

charge = Stripe::Charge.create(
  :amount => source.amount,
  :currency => source.currency,
  :source => source.id,
  :description => source.owner.email
)
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

source=stripe.Source.create(
  type='bitcoin',
  amount=1000,
  currency='usd',
  owner={
    "email":'payinguser+fill_now@example.com'
  }
)

charge = stripe.Charge.create(
    amount=source.amount,
    currency=source.currency,
    source=source.id,
    description=source.owner.email
)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

$source = \Stripe\Source::create(array(
  "type" => "bitcoin",
  "amount" => 1000,
  "currency" => "usd",
  "owner" => array(
    "email" => "payinguser+fill_now@example.com"
  )
));

$charge = \Stripe\Charge::create(array(
  "amount" => $source->amount,
  "currency" => $source->currency,
  "source" => $source->id,
  "description" => "Example charge"
));
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

Map<String, Object> ownerParams = new HashMap<String, Object>();
ownerParams.put("email", "payinguser+fill_now@example.com");

Map<String, Object> sourceParams = new HashMap<String, Object>();
sourceParams.put("type", "bitcoin");
sourceParams.put("amount", 1000);
sourceParams.put("currency", "usd");
sourceParams.put("owner", ownerParams);

Source source = Source.create(sourceParams);

Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("source", source.getId());
chargeParams.put("amount", source.getAmount());
chargeParams.put("currency", source.getCurrency());
Charge charge = Charge.create(chargeParams);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

var source = stripe.source.create({
  type: "bitcoin",
  amount: 1000,
  currency: "usd",
  owner: {
    email: "payinguser+fill_now@example.com"
  }
}, function(err, source) {
  stripe.charges.create({
    amount: source.amount,
    currency: source.currency,
    source: source.id,
    description: "Example charge"
  }, function(err, charge) {
    // asynchronously called
  });
});

// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.Key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

sourceParams := &stripe.SourceObjectParams{
  Type: "bitcoin",
  Amount: 1000,
  Currency: "usd",
  Owner: &stripe.SourceOwnerParams{
    Email: "payinguser+fill_now@example.com",
  },
}

s, err := source.New(sourceParams)

if err != nil {
  // handle error
  return;
}

chargeParams := &stripe.ChargeParams{
  Amount:   source.Amount,
  Currency: source.Currency,
}
chargeParams.SetSource(s.ID)

ch, err := charge.New(chargeParams)

if err != nil {
  // handle error
  return;
}

A source can also be linked to a Customer object and used to create a charge for that customer. Only one charge can be made against the source that has been attached to a customer, and it must be for the same amount and currency as the source.

Managing refunds

Like any other type of payment, Bitcoin payments can be refunded through either the Dashboard or API. However, with Bitcoin, the Bitcoin address where to return the funds needs to be communicated by the customer. By default, as soon as a refund is created (or the source is canceled and funds need to be returned), we will automatically contact the customer at the email address provided when you created the source object. Once the customer provides us with their Bitcoin address, we will process the refund automatically.

Mispayments

The customer is responsible for sending the correct amount of bitcoin to fill the source. While uncommon, it is possible for a customer to send an unexpected amount that prevents a payment from being completed—referred to as mispayment. These possibilities are:

  1. The customer sends too few bitcoin so the payment cannot be completed
  2. The customer sends too many bitcoin and needs to be partially refunded
  3. The customer sends the correct amount of bitcoin but they send it belatedly, or there’s a network error such that the source token is never posted to your server

You can help avoid the third possibility of mispayments through the use of webhooks. You can configure your integration to receive source.chargeable events, then subsequently create charges from those sources.

All mispayments are handled automatically by Stripe. When a source is charged, any unused bitcoin received in excess will be returned to the customer automatically (after collecting their refund address as described in the previous section). Similarly if a source is never charged, it eventually gets canceled and any unused bitcoin is also returned the customer automatically.

Webhook events

A successful Bitcoin payment generates the charge.succeeded event, which is the same as a successful credit card charge. There are also events specific to Bitcoin sources:

  • source.chargeable: Occurs whenever a source is filled and has become chargeable
  • source.canceled: Occurs whenever a source has been canceled

You can refer to our webhooks documentation for more information on receiving webhook notifications.