Skip to content

Commit

Permalink
Adding latest updates
Browse files Browse the repository at this point in the history
  • Loading branch information
tnylea committed Aug 12, 2024
1 parent e815628 commit ff5b6ba
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 101 deletions.
23 changes: 23 additions & 0 deletions collections/themes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"name" : "Anchor",
"cover" : "https://cdn.devdojo.com/images/august2024/theme-anchor.jpeg",
"slug" : "anchor",
"description": "Anchor is the default theme available. This theme is clean with a sidebar app layout design.",
"repo" : "https://github.com/thedevdojo/wave"
},
{
"name" : "Blank",
"cover" : "https://cdn.devdojo.com/images/august2024/theme-blank.jpeg",
"slug" : "blank",
"description": "This is a minimal blank theme that can be used to easily modify. It also contains a sidebar app layout.",
"repo" : "https://github.com/thedevdojo/wave"
},
{
"name" : "Cove",
"cover" : "https://cdn.devdojo.com/images/august2024/theme-cove.jpeg",
"slug" : "cove",
"description": "This blue colored theme is a good template for showcasing your SaaS. Top bar app layout",
"repo" : "https://github.com/thedevdojo/wave"
}
]
117 changes: 81 additions & 36 deletions content/docs/features/billing.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,42 @@ prevURL: '/docs/features/collections'
Wave integrates seamlessly with <a href="https://stripe.com" target="_blank" class="underline">Stripe</a> or <a href="https://paddle.com" target="_blank" class="underline">Paddle</a> in order to accept payments in your app. In this section you will learn how to configure and setup billing in your application.

- [Billing](#billing)
- [Selecting Your Payment Provider](#selecting-your-payment-provider)
- [Stripe](#stripe)
- [Add Stripe API Credentials](#add-stripe-api-credentials)
- [Add Stripe Webhook Secret](#add-stripe-webhook-secret)
- [Webhook Warning](#webhook-warning)
- [Testing Sripe Payments and Events](#testing-sripe-payments-and-events)
- [Stripe Customer Portal](#stripe-customer-portal)
- [Paddle](#paddle)
- [Add Paddle API Credentials](#add-paddle-api-credentials)
- [Default payment link](#default-payment-link)
- [Webhooks](#webhooks)
- [Ready to go Live?](#ready-to-go-live)
- [Test Billing Process](#test-billing-process)
- [Paddle Environment](#paddle-environment)
- [Paddle API Credentials](#paddle-api-credentials)
- [Paddle Webhooks](#paddle-webhooks)
- [Paddle Payment link](#paddle-payment-link)
- [Test Billing Process](#test-billing-process)

## Selecting Your Payment Provider

You may choose whichever payment provider fits your needs. To change the payment provider you want to use, you can change the `BILLING_PROVIDER` value inside your application `.env` file. You can set this to **stripe** or **paddle**. This config is stored inside the `config/wave.php` file.

```php
<?php

return [

...
'billing_provider' => env('BILLING_PROVIDER', 'stripe'),
...

];
```

Ok, now that you've selected the payment provider, lets dig in a little deeper into each one to learn how to get it setup.

<a name="stripe"></a>
## Stripe

Stripe is an online payment platform that enables your application to receive payments from customers. Wave utilizes <a href="https://docs.stripe.com/payments/checkout" target="_blank">Stripe Checkout</a>, which is the simplest implementation of accepting payments. In order to get setup you'll need the following credentials.
Wave utilizes <a href="https://docs.stripe.com/payments/checkout" target="_blank">Stripe Checkout</a>, which is the simplest implementation of accepting payments. In order to get setup you'll need the following credentials.

- Stripe Publishable Key
- Stripe Secret Key
Expand Down Expand Up @@ -66,6 +86,18 @@ The **Webhook Secret** key allows stripe to talk to your application and receive
STRIPE_WEBHOOK_SECRET=whsec_75...
```

### Webhook Warning

If your webhook is not setup correctly, it may look like the user purchases and their account never upgrades. Here is a workflow overview of how it works from when the user clicks the **Subscribe to Plan** button.

<img src="https://cdn.devdojo.com/images/august2024/stripe-webhook-workflow-new.png" class="relative w-full rounded-md border border-gray-200" />

As you can see that the webhook gets sent at the same time as a successful payment. So, if the webhook is not setup correctly the user will be redirected to the success page, but their account will not actually be upgraded.

As long as the webhook is setup correctly, the functionality will happen simultaneously, so everything will function correctly. This is probably a good thing to know in case you are having trouble debugging the situation where an account never gets upgraded after a successful purchase.

We may change the way that this works down the road, but for now it's good to know about how this functions.

<a name="testing-stripe-payments-events"></a>
### Testing Sripe Payments and Events

Expand Down Expand Up @@ -102,63 +134,76 @@ Here you can customize all the aspects of the customer portal.
<a name="paddle"></a>
## Paddle

<a name="paddle-api-credentials"></a>
### Add Paddle API Credentials

Inside of your Paddle Dashboard you'll see a button under the **Developer Tools** menu, called **Authentication**, click on that button to get your API Authentication Credentials.
Paddle also offers a very simple <a href="https://developer.paddle.com/concepts/sell/self-serve-checkout" target="_blank">Checkout integration</a>. This is the integration that we'll be using. In order to setup this implementation we'll need 4 different keys.

![paddle-authentication.png](https://imgur.com/xdDuVKn.png)
1. Paddle Vendor ID
2. Paddle API Key
3. Paddle Public Secret
4. Paddle Webhook Secret

Along with the **API Auth Code**, you'll also need to get your **Client Side Token**.
### Paddle Environment

On this page you'll find your **Seller ID** and your **API Auth Code**. These are the credentials that you will need to add to your `.env` file for `PADDLE_VENDOR_ID`, `PADDLE_API_KEY` and `PADDLE_CLIENT_SIDE_TOKEN`:
When you are implementing your Paddle integration you will want to test the payment process using <a href="https://sandbox-vendors.paddle.com/" target="_blank" class="font-bold">Sandbox Mode</a>. To do this, you need to specify the `PADDLE_ENV` value inside your `.env` file:

```
PADDLE_VENDOR_ID=9999
PADDLE_API_KEY=YOUR_REALLY_API_KEY_HERE
PADDLE_CLIENT_SIDE_TOKEN=YOUR_CLIENT_SIDE_TOKEN
PADDLE_ENV=sandbox
```

After adding these credentials, your application has been successfully configured with Paddle.
When, you're ready to put your application in production you will change the `PADDLE_ENV` to `production`. Next, we need to add our API credentials.

## Default payment link
### Paddle API Credentials

Wave uses the default Paddle payment link to handle the payment process. You have to set up the default payment link in your Paddle account. To do this, go to your Paddle dashboard and click on **Checkout Settings** scroll down to **Payment Links**.
Inside of your Paddle Dashboard you'll see a button under the **Developer Tools** menu, called **Authentication**, click on that button to get your API Authentication Credentials.

The default payment link should be set to `http://yourdomain.com/settings/subscription`.
<img src="https://cdn.devdojo.com/images/august2024/paddle-api-keys.png" class="w-full rounded-md" />

![](https://imgur.com/zboWobt.png)
From this page, you can get your **Vendor ID (seller ID)**, **API Key**, and **Client-side Token**. To get the API Key and Public key, you'll need to **+ Generate** a new key if you do not have one. Then, click the *Show Key* button in the menu to the right. Paste the key values into your application `.env` file:

```
PADDLE_VENDOR_ID=9999
PADDLE_API_KEY=...
PADDLE_CLIENT_SIDE_TOKEN=...
```

## Webhooks
Next, we need to setup our webhook.

Wave uses Paddle webhooks to handle the payment process. You have to set up the webhooks in your Paddle account. To do this, go to your Paddle dashboard and click on **Developer Tools** -> **Notifications**.
### Paddle Webhooks

![](https://imgur.com/QqJTggu.png)
Paddle webhooks are used to send events to your application. These events include a user subscription being created, cancelled, updated, or a payment has failed. In order for your app to handle these events correctly we have to setup a webhook. To do this, go to your Paddle dashboard and click on **Developer Tools** -> **Notifications**.

Make sure to select the `subscription.cancelled` event so that Wave can handle the subscription cancellation process in case a user cancels their subscription or their payment fails.
<img src="https://cdn.devdojo.com/images/august2024/paddle-webhook.png" class="w-full" />

> **Note**: Wave currently only supports the `subscription.cancelled` event. More events will be supported in the future.
From the slide over menu, you will need to add your application URL and the path `webhook/paddle`. If you are in Sandbox mode, to test this out you may need to Publicly Share your local site and update that public URL inside the field.

#### Ready to go Live?
You will also need to check all the events that we want to send to our application.

When you are ready to go live and take live payments you'll want to change the `PADDLE_ENV` from `sandbox` to `live`, and you'll be ready to accept live payments 💵
<img src="https://cdn.devdojo.com/images/august2024/paddle-events.png" class="w-full" />

<a name="test-billing"></a>
### Test Billing Process
Make sure to select the `subscription.cancelled`, `subscription.created`, `subscription.updated`, and `transaction.payment_failed`. Those are the events that we handle on Waves end.

Before you can test out the full billing process, you will need to add a few [Subscription Plans](/docs/features/subscription-plans).
Finally, we need to set our payment link.

**Notice**: If you are using a Sandbox account, you will need to test your app from a `http://localhost` URL. The best way to do this is to utilize the laravel **Artisan Serve** command, or you can use [Laravel Sail](https://www.youtube.com/watch?v=WGhiY5xamms) docker image to serve up your app from a localhost URL.
### Paddle Payment link

After adding subscription plans and configuring your application with your Paddle API keys, you will now be able to test out the billing process using the following credentials:
Paddle needs to know your default payment link. This is users will be redirected when they cancel or make upates on Paddles end. To update this payment link, from the left menu under **Checkout**, click the **Checkout Settings** button and then scroll down to **Payment Links**.

The default payment link should be set to `http://yourdomain.com/settings/subscription`.

<img src="https://cdn.devdojo.com/images/august2024/payment-link.png" class="w-full" />

## Test Billing Process

You can test out the Stripe or Paddle implementation using the following credentials:

```
Credit Card: 4242 4242 4242 4242
Expiration: Any Future Date
CVC: Any 3 digit code
CVC: Any three digit code
```

---
More information below about Test Credit Cards for each provider:

- <a href="https://docs.stripe.com/testing#cards" target="_blank">Stipe Test Cards</a>
- <a href="https://developer.paddle.com/concepts/payment-methods/credit-debit-card#test-payment-method" target="_blank">Paddle Test Cards</a>

After adding your Paddle API credentials, you'll need to configure your app with a few [Subscription Plans](/docs/features/subscription-plans) in order to test out the whole process. Let's move on to the [next step](/docs/features/subscription-plans) where you will learn how to do this.
Before we can fully test out your integration, you will need to add a few <a href="{ url('/docs/features/subscription-plans') }">Subscription Plans</a>. We'll cover that next.
10 changes: 5 additions & 5 deletions includes/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
active='docs';
}
if(window.location.pathname.includes('templates')){
if(window.location.pathname.includes('themes')){
fullWidth = true;
active='templates';
active='themes';
}
$watch('mobileMenu', function(value){
Expand Down Expand Up @@ -136,12 +136,12 @@
:class="{ 'w-full left-0' : active=='home', 'w-0 left-1/2' : active!='home' }"
class="absolute bottom-0 px-1.5 h-px duration-300 ease-out translate-y-1.5"><span class="block relative w-full h-px bg-gradient-to-r opacity-30 md:from-white/0 md:via-white md:to-white/0"></span></span>
</button>
<button hx-get="{ url('/templates') }" hx-target="#site-content" hx-select="#site-content" hx-ext="alpine-morph" hx-swap="morph" hx-push-url="true" @click="active='templates'; fullWidth=true; scrollTop(); resetProgressWidth(); route=$el.getAttribute('hx-get'); mobileMenu=false" data-templates @mouseenter="calculateMarkerPosition($el); hovered='templates'"
:class="{ 'text-gray-900' : hovered == 'templates', 'text-gray-400': hovered != 'templates' }"
<button hx-get="{ url('/themes') }" hx-target="#site-content" hx-select="#site-content" hx-ext="alpine-morph" hx-swap="morph" hx-push-url="true" @click="active='themes'; fullWidth=true; scrollTop(); resetProgressWidth(); route=$el.getAttribute('hx-get'); mobileMenu=false" data-themes @mouseenter="calculateMarkerPosition($el); hovered='themes'"
:class="{ 'text-gray-900' : hovered == 'themes', 'text-gray-400': hovered != 'themes' }"
class="inline-flex relative flex-shrink-0 justify-center items-center px-3 py-3 my-2 w-full h-full leading-tight text-center transition-colors md:rounded-full md:py-2 group md:my-0 md:w-auto md:text-center">
<span>Themes</span>
<span
:class="{ 'w-full left-0' : active=='templates', 'w-0 left-1/2' : active!='templates' }"
:class="{ 'w-full left-0' : active=='themes', 'w-0 left-1/2' : active!='themes' }"
class="absolute bottom-0 px-1.5 h-px duration-300 ease-out translate-y-1.5"><span class="block relative w-full h-px bg-gradient-to-r opacity-30 md:from-white/0 md:via-white md:to-white/0"></span></span>
</button>
<button href="/components" @mouseenter="calculateMarkerPosition($el); hovered='components'"
Expand Down
61 changes: 1 addition & 60 deletions pages/templates.html
Original file line number Diff line number Diff line change
@@ -1,60 +1 @@
<layout title="Static Starter Template" src="main.html">

<div x-data class="max-w-6xl px-6 pt-12 pb-20 mx-auto xl:px-0">

<div class="flex flex-col py-12 text-center text-white lg:py-16">
<h2 class="font-book font-styling w-full font-display mb-2 leading-none text-left md:text-center text-[3rem] lg:text-[3.5rem] pistol-font tracking-tight md:leading-[120%] font-gradient">Templates</h2>
<p class="sans text-left w-full md:text-center max-w-3xl mx-auto opacity-60 text-[1.125rem] md:leading-[1.5] text-slate-11 font-normal">Hand-crafted HTML templates to help you build your next project.</p>
</div>

<div class="grid items-center justify-center w-full h-auto grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 md:gap-10">

<ForEach collection="templates" as="template">
<div x-data @click="window.dispatchEvent(new CustomEvent('show-template-preview', { detail : { url: '{template.url}' }})); window.dispatchEvent(new CustomEvent('set-active-template', { detail: { slug: '{template.slug}' }}));"
class="flex w-full overflow-hidden transition-all duration-75 ease-out border rounded-md cursor-pointer border-neutral-700 sm:rounded-xl sm:shadow-lg group opacity-95 hover:opacity-100">
<div class="flex flex-col">
<div class="relative overflow-hidden">
<img src="{template.cover}" class="w-full relative z-10 group-hover:scale-[1.02] transition-all ease-out duration-300 overflow-hidden h-auto">

</div>
<div class="relative ">
<div class="relative bottom-0 left-0 z-20 w-full px-5 py-2 leading-none bg-white border-t border-b bg-opacity-90 border-neutral-800">
<code class="font-mono text-xs font-medium text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-pink-400 via-green-400">
<If condition="template.slug != 'starter'">
<span>static new project --{template.slug}</span>
</If>
<If condition="template.slug == 'starter'">
<span>static new project</span>
</If>
</code>
</div>
<div class="flex items-center w-full p-5 pb-2">

<div class="relative flex flex-col items-center justify-center h-auto">
<h3 class="relative flex items-center -mt-px text-lg font-semibold leading-none text-gray-200 group-hover:text-gray-100 ">
<span>{template.name}</span>
<svg class="group-hover:translate-x-0 -translate-x-1 w-2.5 h-2.5 stroke-current ml-1 -rotate-45 transition-all ease-in-out duration-200 transform" viewBox="0 0 13 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><g id="svg" transform="translate(0.666667, 2.333333)" stroke="currentColor" stroke-width="2.4"><g><polyline class="transition-all duration-200 ease-out opacity-0 delay-0 group-hover:opacity-100" points="5.33333333 0 10.8333333 5.5 5.33333333 11"></polyline><line class="transition-all duration-200 ease-out transform -translate-x-1 opacity-0 group-hover:translate-x-0 group-hover:opacity-100 group-hover:ml-0" x1="10.8333333" y1="5.5" x2="0.833333333" y2="5.16666667"></line></g></g></g></svg>
</h3>
</div>
</div>
<p class="px-5 text-sm text-white/60 group-hover:text-white/80">{template.description}</p>


<div class="px-5 pt-1 pb-5">
<div class="flex items-center justify-center w-full px-3 py-2 mt-3 space-x-1.5 text-sm font-medium text-center duration-300 ease-out rounded-md bg-neutral-100 group-hover:bg-white text-neutral-800">
<span class="duration-100 ease-out">Live Preview</span>
<svg class="w-3.5 h-3.5 duration-100 ease-out stroke-current" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
</div>
</div>

</div>
</div>
</div>
</ForEach>
</div>

<include src="template-previews.html"></include>

</div>

</layout>
<layout url="{ url('/themes') }" src="redirect.html"></layout>
Loading

0 comments on commit ff5b6ba

Please sign in to comment.