lunarphp/stripe

Lunar 的 Stripe 支付驱动程序。

维护者

详细信息

github.com/lunarphp/stripe

源代码

安装次数: 16,719

依赖者: 2

建议者: 0

安全性: 0

星标: 20

关注者: 5

分支: 13

类型:项目


README

此插件使您能够在 Lunar 店面上启用 Stripe 支付。

Alpha 版本

此插件目前处于 Alpha 版,尽管我们已尽一切努力确保其按预期工作,但只有在添加更多测试并证明其有效性后,才会将其视为 Alpha 版本。

所需测试

  • 从 Stripe 获取成功的收费响应。
  • 从 Stripe 获取失败的收费响应。
  • 测试 手动 配置是否做出适当反应。
  • 测试 自动 配置是否做出适当反应。
  • 确保交易正确存储在数据库中。
  • 确保在使用同一购物车时,支付意图不会重复。
  • 确保根据 Stripe 的响应返回适当的响应。
  • 测试退款和部分退款创建预期的交易。
  • 确保我们可以手动释放付款或部分付款并处理不同的响应。

最低要求

  • Lunar 1.x
  • 拥有具有密钥的 Stripe 账户

可选要求

  • Laravel Livewire(如果使用前端组件)
  • Alpinejs(如果使用前端组件)
  • JavaScript 框架

安装

需要 composer 包

composer require lunarphp/stripe

发布配置

这将发布配置到 config/lunar/stripe.php

php artisan vendor:publish --tag=lunar.stripe.config

发布视图(可选)

Lunar Stripe 包含一些辅助组件,您可以在结账时使用,如果您打算编辑它们提供的视图,则可以发布它们。

php artisan vendor:publish --tag=lunar.stripe.components

启用驱动程序

config/lunar/payments.php 中设置驱动程序

<?php

return [
    // ...
    'types' => [
        'card' => [
            // ...
            'driver' => 'stripe',
        ],
    ],
];

添加您的 Stripe 凭证

请确保您已在 config/services.php 中设置了 Stripe 凭证

'stripe' => [
    'key' => env('STRIPE_SECRET'),
    'public_key' => env('STRIPE_PK'),
    'webhooks' => [
        'lunar' => env('LUNAR_STRIPE_WEBHOOK_SECRET'),
    ],
],

密钥可以在您的 Stripe 账户中找到 https://dashboard.stripe.com/apikeys

配置

以下是在 config/lunar/stripe.php 中使用的此包的可用的配置选项列表

后端使用

创建支付意图

use \Lunar\Stripe\Facades\Stripe;

Stripe::createIntent(\Lunar\Models\Cart $cart, $options = []);

此方法将根据购物车创建 Stripe 支付意图,并将生成的 ID 添加到元数据以供以后检索。如果购物车已存在支付意图,则将从 Stripe 获取它并返回该意图,以避免创建重复的支付意图。

您可以通过传递任何所需的附加参数,默认情况下发送以下参数:

[
    'amount' => 1099,
    'currency' => 'GBP',
    'automatic_payment_methods' => ['enabled' => true],
    'capture_method' => config('lunar.stripe.policy', 'automatic'),
    // If a shipping address exists on a cart
    // $shipping = $cart->shippingAddress
    'shipping' => [
        'name' => "{$shipping->first_name} {$shipping->last_name}",
        'phone' => $shipping->contact_phone,
        'address' => [
            'city' => $shipping->city,
            'country' => $shipping->country->iso2,
            'line1' => $shipping->line_one,
            'line2' => $shipping->line_two,
            'postal_code' => $shipping->postcode,
            'state' => $shipping->state,
        ],
    ]
]
$paymentIntentId = $cart->meta['payment_intent']; // The resulting ID from the method above.
$cart->meta->payment_intent;

获取现有的支付意图

use \Lunar\Stripe\Facades\Stripe;

Stripe::fetchIntent($paymentIntentId);

同步现有的意图

如果已创建支付意图并且购物车有更改,则您将想要更新意图,以便它具有正确的总计。

use \Lunar\Stripe\Facades\Stripe;

Stripe::syncIntent(\Lunar\Models\Cart $cart);

更新现有的意图

当您想更新支付意图的某些属性时,而不需要重新计算购物车。

请参阅 https://docs.stripe.com/api/payment_intents/update

use \Lunar\Stripe\Facades\Stripe;

Stripe::updateIntent(\Lunar\Models\Cart $cart, [
    'shipping' => [/*..*/]
]);

取消现有的意图

如果您需要取消支付意图,则可以这样做。您需要提供一个有效的理由,这些理由可以在 Stripe 文档中找到:https://docs.stripe.com/api/payment_intents/cancel

Lunar Stripe 包含一个 PHP Enum 来简化此操作

use Lunar\Stripe\Enums\CancellationReason;

CancellationReason::ABANDONED;
CancellationReason::DUPLICATE;
CancellationReason::REQUESTED_BY_CUSTOMER;
CancellationReason::FRAUDULENT;
use Lunar\Stripe\Facades\Stripe;
use Lunar\Stripe\Enums\CancellationReason;

Stripe::cancelIntent(\Lunar\Models\Cart $cart, CancellationReason $reason);

更新 Stripe 上的地址

因此,您无需手动指定所有发货地址字段,可以使用辅助函数来帮您完成。

use \Lunar\Stripe\Facades\Stripe;

Stripe::updateShippingAddress(\Lunar\Models\Cart $cart);

费用

检索特定费用

use \Lunar\Stripe\Facades\Stripe;

Stripe::getCharge(string $chargeId);

获取支付意图的所有费用

use \Lunar\Stripe\Facades\Stripe;

Stripe::getCharges(string $paymentIntentId);

Webhooks

此插件提供了一个可选的webhook,您可以将其添加到Stripe中。您可以在Stripe网站上阅读如何操作的指南https://stripe.com/docs/webhooks/quickstart

您应该监听的事件是payment_intent.payment_failedpayment_intent.succeeded

webhook的路径将是http:://yoursite.com/stripe/webhook

您可以在config/lunar/stripe.php中自定义webhook的路径。

您还需要将webhook签名密钥添加到services.php配置文件中。

<?php

return [
    // ...
    'stripe' => [
        // ...
        'webhooks' => [
            'payment_intent' => '...'
        ],
    ],
];

如果您不想使用webhook,或者想手动处理订单,您也可以这样做。

$cart = CartSession::current();

// With a draft order...
$draftOrder = $cart->createOrder();
Payments::driver('stripe')->order($draftOrder)->withData([
    'payment_intent' => $draftOrder->meta['payment_intent'],
])->authorize();

// Using just the cart...
Payments::driver('stripe')->cart($cart)->withData([
    'payment_intent' => $cart->meta['payment_intent'],
])->authorize();

店面示例

首先,我们需要设置后端API调用以获取或创建意图,这并不是Vue特定的,如果您使用Livewire,它可能有所不同。

use \Lunar\Stripe\Facades\Stripe;

Route::post('api/payment-intent', function () {
    $cart = CartSession::current();

    $cartData = CartData::from($cart);

    if ($paymentIntent = $cartData->meta['payment_intent'] ?? false) {
        $intent = StripeFacade::fetchIntent($paymentIntent);
    } else {
        $intent = StripeFacade::createIntent($cart);
    }

    if ($intent->amount != $cart->total->value) {
        StripeFacade::syncIntent($cart);
    }
        
    return $intent;
})->middleware('web');

Vuejs

这仅使用Stripe的支付元素,有关更多信息,请查看Stripe指南

支付组件

<script setup>
const { VITE_STRIPE_PK } = import.meta.env

const stripe = Stripe(VITE_STRIPE_PK)
const stripeElements = ref({})

const buildForm = async () => {
    const { data } = await axios.post("api/payment-intent")

    stripeElements.value = stripe.elements({
        clientSecret: data.client_secret,
    })

    const paymentElement = stripeElements.value.create("payment", {
        layout: "tabs",
        defaultValues: {
            billingDetails: {
                name: `{$billingAddress.value.first_name} {$billingAddress.value?.last_name}`,
                phone: billingAddress.value?.contact_phone,
            },
        },
        fields: {
            billingDetails: "never",
        },
    })

    paymentElement.mount("#payment-element")
}

onMounted(async () => {
    await buildForm()
})

// The address object can be either passed through as props or via a second API call, but it should likely come from the cart.

const submit = async () => {
    try {
        const address = {...}

        const { error } = await stripe.confirmPayment({
            //`Elements` instance that was used to create the Payment Element
            elements: stripeElements.value,
            confirmParams: {
                return_url: 'http://yoursite.com/checkout/complete',
                payment_method_data: {
                    billing_details: {
                        name: `{$address.first_name} {$address.last_name}`,
                        email: address.contact_email,
                        phone: address.contact_phone,
                        address: {
                            city: address.city,
                            country: address.country.iso2,
                            line1: address.line_one,
                            line2: address.line_two,
                            postal_code: address.postcode,
                            state: address.state,
                        },
                    },
                },
            },
        })
    } catch (e) {
    
    }
}
</script>
<template>
    <form @submit.prevent="submit">
        <div id="payment-element">
            <!--Stripe.js injects the Payment Element-->
        </div>
    </form>
</template>

贡献

欢迎贡献,如果您打算添加功能,请首先提交一个问题,以便我们确定是否应该包含它。

测试

使用MockClient类来模拟Stripe API将返回的响应。