Kasiyer Iyzico
- Introduction
- Installation
- Configuration
- Customers
- Subscriptions
- Subscription Trials
- Handling Iyzico Webhooks
- Single Charges
- Transactions
- Testing
Introduction
Kasiyer Iyzico is currently in beta. Using Kasiyer Iyzico in production at your own risk. Thanks for your help with testing and reporting bugs.
Kasiyer makes easy Iyzico's subscription billing services for your Laravel application. It handles almost requirements for subscription billing code. In addition to subscription management, Kasiyer also provides swapping subscriptions, cancelling subscription, single charges and refunds.
While using Kasiyer we strongly recommend you also refer to Iyzico's github page and API documentation
Installation
Install Kasiyer package with Composer.
composer require frkcn/kasiyer
Database Migrations
The Kasiyer service provider registers its own database migration directory. Don't forget to migrate your database after installing the package. The Kasiyer migrations will create a new subscriptions and customers table.
php artisan migrate
If you need to overwrite the Kasiyer migrations, you can publish them using Artisan command:
php artisan vendor:publish --tag="kasiyer-migrations"
If you want to prevent Kasiyer's migrations from running entirely, use ignoreMigrations
. This method should be called in the register
method of your AppServiceProvider
:
use Frkcn\Kasiyer\Kasiyer;
Kasiyer::ignoreMigrations();
Configuration
Billable Model
Before using Kasiyer, you must add the Billable trait to your model definition. This trait provides various methods to creating subscriptions, updating payment card, single charge, refund payment.
use Frkcn\Kasiyer\Billable;
class User extends Authenticatable
{
use Billable;
}
You can use any Authenticatable
model against App\User
class. That's it, you don't need to change anything else.
API Keys
Next, you should configure your Iyzico keys in your .env
file. You can retrieve your Iyzico API keys from the Iyzico merchant panel:
IYZICO_KEY=your-iyzico-private-key
IYZICO_SECRET=your-iyzico-secret-key
Don't forget to change
KASIYER_URL
to production URL in your.env
file when your code running in production.
KASIYER_URL=iyzico-production-baseurl
Currency Configuration
The default Kasiyer currency is Turkish Liras (TRY). You can change the default currency by setting the KASIYER_CURRENCY
environment variable.
KASIYER_CURRENCY=USD
In addition to configuring Kasiyer's currency, you may also specify a locale to be used for checkout form and responses language.
KASIYER_LOCALE=en
Customers
Creating Customers
Before creating subscription or single charge, you should create a customer.
$user = User::find(1);
$customer = $user->customer()->create([
'name' => 'Faruk',
'surname' => 'Can',
'gsm_number' => '+905555555555',
'iyzico_email' => 'faarukcan@gmail.com',
'identity_number' => '11111111110',
'shipping_contact_name' => 'Faruk Can',
'shipping_city' => 'Istanbul',
'shipping_country' => 'Turkey',
'shipping_address' => 'Nisbetiye Cad. No:4',
'shipping_zip_code' => '34340',
'billing_contact_name' => 'Faruk Can',
'billing_city' => 'Istanbul',
'billing_country' => 'Turkey',
'billing_address' => 'Nisbetiye Cad. No:4',
'billing_zip_code' => '34340',
]);
Retrieving Customers
You may use the asIyzicoCustomer method if you want to return the customer object if the customer entity is already a customer within Iyzico.
$iyzicoCustomer = $user->asIyzicoCustomer();
Updating Customers
When you update your customer, Kasiyer will automatically update the customer within Iyzico.
$iyzicoCustomer = $user->customer()
->update([$options]);
Subscriptions
Creating Subscriptions
Most interactions with Iyzico are done through its checkout form. To create a subscription, first retrieve an instance of your billable model, which will be an instance of App\User
. Once you have retrieved the model instance, you may use the newSubscription
method to create the model's subscription checkout form:
$user = User::find(1);
$checkoutForm = $user->newSubscription('default', $default = 'ba304be8-f17a-4f23-8500-f7d47b6927a1')
->returnTo(route('home'))
->create();
return view('billing', ['checkoutForm' => $checkoutForm]);
This response will give you an htmlContent. As soon as you print it into your web-page, the javascript will load iyzico’s libraries and checkout form will be ready. The content of form will be loaded into one of divs below.
<div id="iyzipay-checkout-form" class="responsive"></div>
If you want to show checkout form on pop-up. Add below code:
<div id="iyzipay-checkout-form" class="popup"></div>
Once the user submitted the checkout form, user will be redirected to returnTo
URL. At the same time, a
post request containing the token will be sent to this URL. You should send another request to check whether
the subscription is successfully started or not.
$user = User::find(1);
$user->handleSubscription($request->token);
For more information about checkout form result, consult Iyzico's subscription api.
Checking Subscription Status
Once a user is subscribed to your application, you may check their subscription status using a variety of convenient methods. First, the subscribed
method returns true
if the user has an active subscription, even if the subscription is currently within its trial period:
if ($user->subscribed('default')) {
//
}
You can use subscribed
method for allowing you to filter access to routes and controllers based on the user's subscription status within route middleware
:
public function handle($request, Closure $next)
{
if ($request->user() && !$request->user()->subscribed('default')) {
// This user is not a paying customer.
return redirect('billing');
}
return $next($request);
}
If you want to show your users that they are still on their trial period:
if ($user->subscription('default')->onTrial()) {
//
}
If you would like to know a user subscribed to given plan based on a given Iyzico plan ID:
if ($user->subscribedToPlan($monthly = 'ba304be8-f17a-4f23-8500-f7d47b6927a1', 'default')) {
//
}
The recurring
method may be used to determine if the user is currently subscribed and is no longer within their trial period.
if ($user->subscription('default')->recurring()) {
//
}
Cancelled Subscription Status
To determine if the user was once an active subscriber, but has cancelled their subscription, you may use the cancelled
method:
if ($user->subscription('default')->cancelled()) {
//
}
You may also determine if a user has cancelled their subscription, but are still on their "grace period" until the subscription fully expires. For example, if a user cancels a subscription on March 5th that was originally scheduled to expire on March 10th, the user is on their "grace period" until March 10th. Note that the subscribed method still returns true during this time:
if ($user->subscription('default')->onGracePeriod()) {
//
}
To determine if the user has cancelled their subscription and is no longer within their "grace period", you may use the ended
method:
if ($user->subscription('default')->ended()) {
//
}
Subscription Scopes
Most subscription states are also available as query scopes so that you may easily query your database for subscriptions that are in a given state:
// Get all active subscriptions...
$subscriptions = Subscription::query()->active()->get();
// Get all of the cancelled subscriptions for a user...
$subscriptions = $user->subscriptions()->cancelled()->get();
A complete list of available scopes is available below:
Subscription::query()->active();
Subscription::query()->onTrial();
Subscription::query()->notOnTrial();
Subscription::query()->pastDue();
Subscription::query()->recurring();
Subscription::query()->ended();
Subscription::query()->cancelled();
Subscription::query()->notCancelled();
Subscription::query()->onGracePeriod();
Subscription::query()->notOnGracePeriod();
Past Due Status
If a payment fails for a subscription, it will be marked as past_due
. When your subscription is in this state it will not be active until you retry payment attempt and result returns true
. You may determine if a subscription is past due using the pastDue
method on the subscription instance:
if ($user->subscription('default')->pastDue()) {
//
}
When a subscription is past due, you should instruct the user to update their payment information or make sure there is enough limit on your customer's card.
If you would like subscriptions to still be considered active when they are past_due
, you may use the keepPastDueSubscriptionsActive
method provided by Kasiyer. Typically, this method should be called in the register
method of your AppServiceProvider
:
use Frkcn\Kasiyer\Kasiyer;
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Kasiyer::keepPastDueSubscriptionsActive();
}
When a subscription is in a
past_due
state it cannot be changed until you retry payment attempt successfully. Therefore, theswap
methods will throw an exception when the subscription is in apast_due
state.
Updating Payment Information
In some situations(geting payment errors, card expiration, etc.) customers may need to update the card information. This method allows customers to create a checkout form to update the card. The new card is validated with a charge of 1 TL (USD, EUR) and this amount is refunded immediately.
$user = User::find(1);
$updateCheckoutForm = $user->subscription('default')
->returnTo(route('home'))
->cardUpdate();
This response will give you an htmlContent. As soon as you print it into your web-page, the javascript will load iyzico’s libraries and checkout form will be ready. The content of form will be loaded into one of divs below.
<div id="iyzipay-checkout-form" class="responsive"></div>
Once the user submitted the update checkout form, user will be redirected to returnTo URL. This method will post a token to the specified returnTo
URL if the status is success
, it means that the card is
successfully updated.
Retry Payment Attempt
If a payment of a recurring payment is not successful
, this method retries payment attempt.
$user = User::find(1);
$user->subscription('default')->retry();
Changing Plans
After a user has subscribed to your application, they may occasionally want to change to a new subscription plan. To swap a user to a new subscription, you should pass the Iyzico plan's identifier to the subscription's swap
method:
$user = User::find(1);
$user->subscription('default')->swap($premium = 'ba304be8-f17a-4f23-8500-f7d47b6927a1');
If the user is on trial, the trial period will be maintained.
If you would like to swap plans and cancel any trial period the user is currently on, you may use the skipTrial method:
$user = User::find(1);
$user->subscription('default')
->skipTrial()
->swap($premium = 'ba304be8-f17a-4f23-8500-f7d47b6927a1');
If you want to update plan name when swapping plan, simply update:
$subscription->update([
'name' => 'premium',
]);
Customers can change the pricing plan of a subscription if only they are configured under the same product.
Note:PaymentInterval
andPaymentIntervalCount
of new and old pricing plans must be same.
Cancelling Subscriptions
To cancel a subscription, call the cancel
method on the user's subscription:
$user = User::find(1);
$user->subscription('default')->cancel();
When a subscription is cancelled
, Kasiyer will automatically set the ends_at
column in your database. This column is used to know when the subscribed
method should begin returning false
. For example, if a customer cancels a subscription on March 1st, but the subscription was not scheduled to end until March 5th, the subscribed method will continue to return true
until March 5th.
You may determine if a user has cancelled
their subscription but are still on their "grace period" using the onGracePeriod
method:
if ($user->subscription('default')->onGracePeriod()) {
//
}
If you wish to cancel a subscription immediately, you may call the cancelNow
method on the user's subscription:
$user->subscription('default')->cancelNow();
Iyzico's subscriptions cannot be resumed after cancellation. If your customer wishes to resume their subscription, they will have to subscribe to a new subscription.
Subscriptions Trials
With Payment Method Up Front
Yo must to define how many trial days your plan's receive in the Iyzico merchant dashboard. Kasiyer automatically detect if subscription plan has trials.
If the customer's subscription is not cancelled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date.
You may determine if the user is within their trial period using either the onTrial
method of the user instance or the onTrial
method of the subscription instance. The two examples below have identical behavior:
if ($user->onTrial('default')) {
//
}
if ($user->subscription('default')->onTrial()) {
//
}
Without Payment Method Up Front
If you would like to offer trial periods without collecting the user's payment method information up front, you may set the trial_ends_at
column on the customer record to your desired trial ending date. This is typically done during customer
creation:
$user = User::find(1);
$user = $user->customer()->create([
// Other user properties...
'trial_ends_at' => now()->addDays(10),
]);
Kasiyer refers to this type of trial as a "generic trial", since it is not attached to any existing subscription. The onTrial
method on the User instance will return true if the current date is not past the value of trial_ends_at
:
if ($user->onTrial()) {
// User is within their trial period...
}
You may also use the onGenericTrial
method if you wish to know specifically that the user is within their "generic" trial period and has not created an actual subscription yet:
if ($user->onGenericTrial()) {
// User is within their "generic" trial period...
}
Once you are ready to create an actual subscription for the user, you may use the newSubscription
method as usual:
$user = User::find(1);
$checkoutForm = $user->newSubscription('default', $default = 'ba304be8-f17a-4f23-8500-f7d47b6927a1')
->returnTo(route('home'))
->create();
return view('billing', ['checkoutForm' => $checkoutForm]);
There is no way to extend or modify a trial period on a Iyzico subscription after it has been created.
Handling Iyzico Webhooks
Iyzico can notify your application of SubscriptionOrderSuccess
and SubscriptionOrderFailed
events via webhooks. By default, a route that points to Kasiyer's webhook controller is configured through the Kasiyer service provider. This controller will handle all incoming webhook requests.
To ensure your application can handle Iyzico webhooks, be sure to configure the webhook URL in the Iyzico merchant control panel or contact Iyzico entegration support. By default, Kasiyer's webhook controller listens to the /iyzico/webhook
URL path. The full list of all webhooks you should configure in the Iyzico control panel are:
- Subscription Order Success
- Subscription Order Failed
Webhook Event Handlers
Kasiyer emits a Frkcn\Kasiyer\Events\WebhookReceived
event when a webhook is received, and a Frkcn\Kasiyer\Events\WebhookHandled
event when a webhook was handled. Both events contain the full payload of the Iyzico webhook.
Webhooks & CSRF Protection
Since Iyzico webhooks need to bypass Laravel's CSRF protection, be sure to list the URI as an exception in your VerifyCsrfToken
middleware or list the route outside of the web
middleware group:
protected $except = [
'iyzipay/*',
];
Failed Subscriptions
What if a customer's credit card expires? No worries - Kasiyer's Webhook controller will change the customer's subscription status to pastDue
for you. Failed payments will automatically be captured and handled by the controller. The controller will change the customer's subscription status to pastDue
when Iyzico determines the subscription has failed.
Iyzico only one time attempt to charging subscription payment. You need to decide to cancelling subscription or not after payment failed.
Single Charges
Simple Charge
If you would like to make a "one off" charge against a customer, you may use the charge method on a billable model instance to generate a checkout form for the charge. The charge method accepts the charge amount (float) or (integer) as its first argument and a charge description as its second argument:
$user = User::find(1);
$checkoutForm = $user->setBasketItem([
'id' => 'A123',
'name' => 'Product Title',
'category' => 'Product Category',
'price' => 12.99,
])
->returnTo(route('home'))
->charge();
return view('pay', ['checkoutForm' => $checkoutForm]);
After generating the checkout form, this response will give you an htmlContent. As soon as you print it into your web-page, the javascript will load iyzico’s libraries and checkout form will be ready. The content of form will be loaded into one of divs below.
<div id="iyzipay-checkout-form" class="responsive"></div>
If you want to show checkout form on pop-up. Add below code:
<div id="iyzipay-checkout-form" class="popup"></div>
Once the user submitted the checkout form, user will be redirected to returnTo URL. At the same time, a post request containing the token will be sent to this URL. You should send another request to check whether the charge is successfully started or not.
$result = $user->handleCharge($request->token);
Charges happen in the currency specified in the kasiyer.currency
configuration option. By default, this is set to TRY. You may override the default currency by setting the KASIYER_CURRENCY
in your .env
file:
KASIYER_CURRENCY=USD
Still you need to create a customer with shipping and billing information before charging process.
Refunding Payments
If you need to refund a Iyzico payment, you may use the refund
method. This method accepts the Iyzico Payment ID as its first argument.
$user->refund("12222108");
You may also optionally specify a specific amount to refund as well as a reason for the refund:
$user->refund(
"12222108", 5.00, 'Unused product time'
);
Transactions
You may easily retrieve an array of a billable model's subscription transactions using the transactions
method:
$transactions = $user->transactions();
When listing the transactions for the customer, you may use the transaction's helper methods to display the relevant transaction information. For example, you may wish to list every transaction in a table.
<table>
@foreach ($transactions as $transaction)
<tr>
<td>{{ $transaction->date()->toFormattedDateString() }}</td>
<td>{{ $transaction->amount() }}</td>
<td>{{ $transaction->currency() }}</td>
<td>{{ $transaction->status() }}</td>
</tr>
@endforeach
</table>
Past & Upcoming Payments
You may use the lastPayment
and nextPayment
methods to display a customer's past or upcoming payments for recurring subscriptions:
$subscription = $user->subscription('default');
$lastPayment = $subscription->lastPayment();
$nextPayment = $subscription->nextPayment();
Both of these methods will return an instance of Frkcn\Kasiyer\Payment; however, nextPayment
will return null when the billing cycle has ended (such as when a subscription has been cancelled):
Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->format('d/m/Y') }}
Testing
Be sure using sandbox cridentials when testing.
In order to test a variety of billing scenarios, such as credit card denials and failures, you may use the vast range of testing card numbers provided by Iyzico.