前回、Stripe APIを使用した支払い機能の実装方法を紹介しました。今回は、支払いの取り消し(払い戻し)機能を実装したので、その手順とコードをこちらで記載しておこうと思います。
今回実施する環境
バージョン: PHP 8.2.9
エディター: vsCode
stripe_paymentsテーブルを作成
払い戻し機能の実装に際しては、まず支払い記録を保存するテーブルを作成する必要があります。このテーブルは、Stripeで行われた支払いに関する情報を保存し、後の取り消し処理にも役立ちます。そこで、まずはstripe_paymentsテーブルを作成し、支払い記録を保存する仕組みを作ります。
php artisan make:migration create_payments_table --create=payments
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('stripe_payments', function (Blueprint $table) {
$table->id('stripe_payment_id');
$table->foreignId('appointment_id')
->nullable()
->references('appointment_id')
->on('appointments')
->onUpdate('cascade')
->onDelete('cascade');
$table->string('charge_id');
$table->decimal('amount', 10, 2);
$table->string('customer_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('stripe_payments');
}
};
appointment_id: ここでは、予約情報が保存されているappointmentsテーブルとStripeの支払い情報を関連付けるために、appointment_idを外部キーとして指定します。
charge_id: stripeの方で支払われた時に作成されるId
amount: 支払われた金額
customer_id: クレジット情報などをstripe側へ送信した際に発行されるId
php artisan migrate
モデルを作成します
php artisan make:model StripePayment
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class StripePayment extends Model
{
use HasFactory;
protected $table = 'stripe_payments';
protected $primaryKey = 'stripe_payment_id';
protected $fillable = [
'appointment_id',
'charge_id',
'amount',
'customer_id'
];
}
stripe_paymentsテーブルデータの作成
支払い情報は、実際に予約が行われた時にstripe_paymentsテーブルに保存されるようにコードを記述していますが、その部分を説明すると本題から外れてしまいます。したがって、テストを行う際にはSeederを使用してデータを挿入し、動作を確認していただくことをお勧めします。
予約の削除
今回は、予約が削除された際にその予約に対する払い戻しを行いたいため、予約IDを引数として受け取り、該当する予約に対して払い戻し処理を実行します。支払い処理はAppointmentControllerでは行わず、専用のServices/DeleteAppointmentクラスを作成して、そこで払い戻しと予約の削除処理を行うようにしています。
class AppointmentController extends Controller
{
protected $storePaymentDetails;
public function __construct(DeleteAppointment $deleteAppointment,)
{
$this->deleteAppointment = $deleteAppointment;
}
<!--予約の実装などがありますが省略します-->
/**
* 予約の削除
*
* @param integer $appId
* @return void
*/
public function deleteAppointment(int $appId)
{
$deleteAppointment = $this->deleteAppointment->deleteAppointment($appId);
return response()->json($deleteAppointment);
}
払い戻し処理を行うファイルを作成
php artisan make:controller Services/DeleteAppointmentController
<?php
namespace App\Services;
use App\Models\Appointment;
use Stripe\Stripe;
use Stripe\Refund; //支払いの処理を行う
use App\Models\StripePayment;
use Exception;
class DeleteAppointment
{
/**
* 予約の削除
*
* @param integer $appId
* @return \Illuminate\Http\JsonResponse
*/
public function deleteAppointment($appId)
{
$appointment = Appointment::query()->find($appId);
if ($appointment) {
//支払い情報の取得
$payment = StripePayment::query()->where('appointment_id', $appId)->first();
try {
if ($payment) {
$this->processRefund($payment);
}
//予約の削除
$appointment->delete();
return response()->json(['message' => 'Deleted successfully',
'refund_id' => $payment ? $payment->charge_id : null]);
} catch (Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Refund failed',
'error' => $e->getMessage()
]);
}
} else {
return response()->json(['message' => 'Record not found']);
}
}
//ここでpaymentの情報を受け取り支払いの処理を行います。
/*
* 返金処理
*
* @param StripePayment $payment
* @return void
*/
private function processRefund($payment)
{
Stripe::setApiKey(env('STRIPE_SECRET'));
$refund = Refund::create([
'charge' => $payment->charge_id,
]);
}
}
実装の概要
今回の実装では、予約(Appointment)が削除された際に、関連する支払い情報を払い戻しする処理を行いました。予約削除時に払い戻しが必要なケースを想定し、StripeのAPIを利用して返金処理を行っています。実際には、予約に紐づく支払い情報があればその払い戻しを実行し、その後予約を削除する流れです。
もし予約以外のトリガーで支払い処理を行いたい場合は、Appointmentに関連する部分を別のモデルに置き換えていただければ、同様の処理が可能です。例えば、注文や購買など、他のシステムでも応用できると思います。