Current section
Files
Jump to
Current section
Files
lib/phoenix_kit_billing/web/provider_settings.html.heex
<div class="container mx-auto px-4 py-6">
<%!-- Header --%>
<.admin_page_header
back={PhoenixKit.Utils.Routes.path("/admin/billing")}
title={gettext("Payment Providers")}
subtitle={gettext("Configure online payment integrations")}
/>
<%!-- Navigation Tabs --%>
<div class="tabs tabs-boxed bg-base-200 p-1 mb-6">
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/billing")}
class="tab"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-2" /> {gettext("General")}
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/billing/providers")}
class="tab tab-active"
>
<.icon name="hero-credit-card" class="w-4 h-4 mr-2" /> {gettext("Payment Providers")}
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/currencies")}
class="tab"
>
<.icon name="hero-currency-dollar" class="w-4 h-4 mr-2" /> {gettext("Currencies")}
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/subscription-types")}
class="tab"
>
<.icon name="hero-clipboard-document-list" class="w-4 h-4 mr-2" /> {gettext("Subscription Types")}
</.link>
</div>
<%!-- Active Providers Summary --%>
<div class="alert alert-info mb-6">
<.icon name="hero-information-circle" class="w-5 h-5" />
<div>
<span class="font-medium">{gettext("Active Providers:")}</span>
<%= if Enum.empty?(@available_providers) do %>
<span class="text-base-content/60">{gettext("None configured")}</span>
<% else %>
<%= for {provider, index} <- Enum.with_index(@available_providers) do %>
<span class="badge badge-success badge-sm ml-1">
{provider |> Atom.to_string() |> String.capitalize()}
</span>
<%= if index < length(@available_providers) - 1 do %>
<span class="mx-1"></span>
<% end %>
<% end %>
<% end %>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
<%!-- Stripe Provider --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
<.icon name="hero-credit-card" class="w-6 h-6 text-primary" />
</div>
<div>
<h2 class="card-title text-lg">Stripe</h2>
<p class="text-xs text-base-content/60">{gettext("Cards, Apple Pay, Google Pay")}</p>
</div>
</div>
<label class="cursor-pointer">
<input
type="checkbox"
class="toggle toggle-primary"
checked={@stripe_enabled}
phx-click="toggle_stripe"
/>
</label>
</div>
<div class="divider my-2"></div>
<form phx-submit="save_stripe" class="space-y-3">
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Secret Key")}</span>
<%= if has_credentials?(@stripe_secret_key) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="password"
name="secret_key"
placeholder="sk_live_..."
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Publishable Key")}</span>
<%= if has_credentials?(@stripe_publishable_key) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="text"
name="publishable_key"
placeholder="pk_live_..."
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Webhook Secret")}</span>
<%= if has_credentials?(@stripe_webhook_secret) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="password"
name="webhook_secret"
placeholder="whsec_..."
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Webhook URL")}</span>
</label>
<div class="join w-full">
<input
type="text"
value={@stripe_webhook_url}
class="input input-bordered input-sm font-mono join-item flex-1 text-xs"
readonly
/>
<button
type="button"
class="btn btn-sm join-item"
onclick={"navigator.clipboard.writeText('#{@stripe_webhook_url}')"}
title={gettext("Copy to clipboard")}
>
<.icon name="hero-clipboard" class="w-4 h-4" />
</button>
</div>
</div>
<div class="card-actions justify-end pt-2">
<button type="submit" class="btn btn-primary btn-sm">{gettext("Save Stripe")}</button>
</div>
</form>
</div>
</div>
<%!-- PayPal Provider --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-info/10 flex items-center justify-center">
<.icon name="hero-currency-dollar" class="w-6 h-6 text-info" />
</div>
<div>
<h2 class="card-title text-lg">PayPal</h2>
<p class="text-xs text-base-content/60">{gettext("PayPal, Venmo, Cards")}</p>
</div>
</div>
<label class="cursor-pointer">
<input
type="checkbox"
class="toggle toggle-info"
checked={@paypal_enabled}
phx-click="toggle_paypal"
/>
</label>
</div>
<div class="divider my-2"></div>
<form phx-submit="save_paypal" class="space-y-3">
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Mode")}</span>
</label>
<select name="mode" class="select select-bordered select-sm">
<option value="sandbox" selected={@paypal_mode == "sandbox"}>
{gettext("Sandbox (Test)")}
</option>
<option value="live" selected={@paypal_mode == "live"}>{gettext("Live (Production)")}</option>
</select>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Client ID")}</span>
<%= if has_credentials?(@paypal_client_id) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="text"
name="client_id"
placeholder="AW..."
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Client Secret")}</span>
<%= if has_credentials?(@paypal_client_secret) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="password"
name="client_secret"
placeholder="EL..."
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Webhook ID")}</span>
<%= if has_credentials?(@paypal_webhook_id) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="text"
name="webhook_id"
placeholder="WH-..."
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Webhook URL")}</span>
</label>
<div class="join w-full">
<input
type="text"
value={@paypal_webhook_url}
class="input input-bordered input-sm font-mono join-item flex-1 text-xs"
readonly
/>
<button
type="button"
class="btn btn-sm join-item"
onclick={"navigator.clipboard.writeText('#{@paypal_webhook_url}')"}
title={gettext("Copy to clipboard")}
>
<.icon name="hero-clipboard" class="w-4 h-4" />
</button>
</div>
</div>
<div class="card-actions justify-end pt-2">
<button type="submit" class="btn btn-info btn-sm">{gettext("Save PayPal")}</button>
</div>
</form>
</div>
</div>
<%!-- Razorpay Provider --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-secondary/10 flex items-center justify-center">
<.icon name="hero-banknotes" class="w-6 h-6 text-secondary" />
</div>
<div>
<h2 class="card-title text-lg">Razorpay</h2>
<p class="text-xs text-base-content/60">{gettext("India payments (UPI, Cards)")}</p>
</div>
</div>
<label class="cursor-pointer">
<input
type="checkbox"
class="toggle toggle-secondary"
checked={@razorpay_enabled}
phx-click="toggle_razorpay"
/>
</label>
</div>
<div class="divider my-2"></div>
<form phx-submit="save_razorpay" class="space-y-3">
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Key ID")}</span>
<%= if has_credentials?(@razorpay_key_id) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="text"
name="key_id"
placeholder="rzp_live_..."
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Key Secret")}</span>
<%= if has_credentials?(@razorpay_key_secret) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="password"
name="key_secret"
placeholder="••••••••••••"
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Webhook Secret")}</span>
<%= if has_credentials?(@razorpay_webhook_secret) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="password"
name="webhook_secret"
placeholder="••••••••••••"
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Webhook URL")}</span>
</label>
<div class="join w-full">
<input
type="text"
value={@razorpay_webhook_url}
class="input input-bordered input-sm font-mono join-item flex-1 text-xs"
readonly
/>
<button
type="button"
class="btn btn-sm join-item"
onclick={"navigator.clipboard.writeText('#{@razorpay_webhook_url}')"}
title={gettext("Copy to clipboard")}
>
<.icon name="hero-clipboard" class="w-4 h-4" />
</button>
</div>
</div>
<div class="card-actions justify-end pt-2">
<button type="submit" class="btn btn-secondary btn-sm">{gettext("Save Razorpay")}</button>
</div>
</form>
</div>
</div>
<%!-- EveryPay Provider --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center">
<.icon name="hero-credit-card" class="w-6 h-6 text-accent" />
</div>
<div>
<h2 class="card-title text-lg">EveryPay</h2>
<p class="text-xs text-base-content/60">{gettext("Card payments (Baltics)")}</p>
</div>
</div>
<label class="cursor-pointer">
<input
type="checkbox"
class="toggle toggle-accent"
checked={@everypay_enabled}
phx-click="toggle_everypay"
/>
</label>
</div>
<div class="divider my-2"></div>
<form phx-submit="save_everypay" class="space-y-3">
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Mode")}</span>
</label>
<select name="mode" class="select select-bordered select-sm">
<option value="test" selected={@everypay_mode == "test"}>{gettext("Test (Demo)")}</option>
<option value="live" selected={@everypay_mode == "live"}>{gettext("Live (Production)")}</option>
</select>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("API Username")}</span>
<%= if has_credentials?(@everypay_api_username) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="text"
name="api_username"
placeholder="api_username"
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("API Secret")}</span>
<%= if has_credentials?(@everypay_api_secret) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="password"
name="api_secret"
placeholder="••••••••••••"
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Account Name")}</span>
<%= if has_credentials?(@everypay_account_name) do %>
<span class="label-text-alt text-success text-xs">
<.icon name="hero-check-circle" class="w-3 h-3 inline" /> {gettext("Set")}
</span>
<% end %>
</label>
<input
type="text"
name="account_name"
placeholder="EUR3D1"
class="input input-bordered input-sm font-mono"
autocomplete="off"
/>
</div>
<div class="form-control">
<label class="label py-1">
<span class="label-text text-xs">{gettext("Callback URL")}</span>
</label>
<div class="join w-full">
<input
type="text"
value={@everypay_webhook_url}
class="input input-bordered input-sm font-mono join-item flex-1 text-xs"
readonly
/>
<button
type="button"
class="btn btn-sm join-item"
onclick={"navigator.clipboard.writeText('#{@everypay_webhook_url}')"}
title={gettext("Copy to clipboard")}
>
<.icon name="hero-clipboard" class="w-4 h-4" />
</button>
</div>
</div>
<div class="card-actions justify-end pt-2">
<button type="submit" class="btn btn-accent btn-sm">{gettext("Save EveryPay")}</button>
</div>
</form>
</div>
</div>
</div>
<%!-- Quick Help --%>
<div class="card bg-base-100 shadow-xl mt-6">
<div class="card-body">
<h2 class="card-title">{gettext("Setup Instructions")}</h2>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4 mt-4">
<div class="bg-base-200 p-4 rounded-lg">
<h3 class="font-medium flex items-center gap-2">
<.icon name="hero-credit-card" class="w-5 h-5 text-primary" /> Stripe
</h3>
<ol class="text-sm text-base-content/70 mt-2 space-y-1 list-decimal list-inside">
<li>{gettext("Create account at stripe.com")}</li>
<li>{gettext("Get API keys from Dashboard → Developers")}</li>
<li>{gettext("Create webhook with events: checkout.session.completed, payment_intent.*")}</li>
<li>{gettext("Copy webhook signing secret")}</li>
</ol>
</div>
<div class="bg-base-200 p-4 rounded-lg">
<h3 class="font-medium flex items-center gap-2">
<.icon name="hero-currency-dollar" class="w-5 h-5 text-info" /> PayPal
</h3>
<ol class="text-sm text-base-content/70 mt-2 space-y-1 list-decimal list-inside">
<li>{gettext("Create app at developer.paypal.com")}</li>
<li>{gettext("Get Client ID and Secret")}</li>
<li>{gettext("Configure webhook with events: CHECKOUT.ORDER.*, PAYMENT.*")}</li>
<li>{gettext("Copy Webhook ID")}</li>
</ol>
</div>
<div class="bg-base-200 p-4 rounded-lg">
<h3 class="font-medium flex items-center gap-2">
<.icon name="hero-banknotes" class="w-5 h-5 text-secondary" /> Razorpay
</h3>
<ol class="text-sm text-base-content/70 mt-2 space-y-1 list-decimal list-inside">
<li>{gettext("Create account at razorpay.com")}</li>
<li>{gettext("Get Key ID and Secret from Settings → API Keys")}</li>
<li>{gettext("Create webhook with events: payment.*, order.paid")}</li>
<li>{gettext("Copy webhook secret")}</li>
</ol>
</div>
<div class="bg-base-200 p-4 rounded-lg">
<h3 class="font-medium flex items-center gap-2">
<.icon name="hero-credit-card" class="w-5 h-5 text-accent" /> EveryPay
</h3>
<ol class="text-sm text-base-content/70 mt-2 space-y-1 list-decimal list-inside">
<li>{gettext("Get API username and secret from the EveryPay merchant portal")}</li>
<li>{gettext("Copy the processing account name (e.g. EUR3D1)")}</li>
<li>{gettext("Set the Callback URL in E-shop settings to the URL above")}</li>
<li>{gettext("Use Test mode against the demo gateway first")}</li>
</ol>
</div>
</div>
</div>
</div>
</div>