ข้ามไปยังเนื้อหาหลัก

ไลบรารี Elixir (omise-elixir)

ไลบรารี omise-elixir มอบอินเทอร์เฟซ Elixir ที่เป็นมาตรฐานสำหรับ Omise API พร้อมด้วย pattern matching, ฟีเจอร์ OTP และการผสานรวม Phoenix ที่ยอดเยี่ยม

การติดตั้ง

การใช้ Mix

เพิ่มลงใน mix.exs ของคุณ:

def deps do
[
{:omise, "~> 0.11"}
]
end

จากนั้นรัน:

mix deps.get

ข้อกำหนด

  • Elixir 1.10 หรือสูงกว่า (รวมถึง Elixir 1.14+)
  • Erlang/OTP 22+ (รวมถึง OTP 25+)
  • Mix สำหรับการจัดการ dependency
  • Phoenix 1.6+ (ไม่บังคับ สำหรับแอปพลิเคชัน Phoenix)

เริ่มต้นอย่างรวดเร็ว

การกำหนดค่าพื้นฐาน

# กำหนดค่า API keys ของคุณ
Omise.configure(
public_key: "pkey_test_123456789",
secret_key: "skey_test_123456789",
api_version: "2019-05-29"
)

การกำหนดค่า Phoenix

# config/config.exs
config :omise,
public_key: System.get_env("OMISE_PUBLIC_KEY"),
secret_key: System.get_env("OMISE_SECRET_KEY"),
api_version: "2019-05-29"

# config/runtime.exs (Phoenix 1.6+)
import Config

config :omise,
public_key: System.fetch_env!("OMISE_PUBLIC_KEY"),
secret_key: System.fetch_env!("OMISE_SECRET_KEY")

ตัวแปรสภาพแวดล้อม

# การพัฒนา/ทดสอบ
export OMISE_SECRET_KEY=skey_test_123456789
export OMISE_PUBLIC_KEY=pkey_test_123456789

# ผลิตภาพ
# export OMISE_SECRET_KEY=skey_live_123456789
# export OMISE_PUBLIC_KEY=pkey_live_123456789

การดำเนินการทั่วไป

การสร้างค่าธรรมเนียม

# สร้างค่าธรรมเนียมด้วย card token
{:ok, charge} = Omise.Charge.create(
amount: 100_000, # 1,000.00 บาท (ในหน่วยสกุลเงินที่เล็กที่สุด)
currency: "THB",
card: "tokn_test_123",
description: "Order #1234",
metadata: %{
order_id: "1234",
customer_name: "John Doe"
}
)

case charge do
%{paid: true} ->
IO.puts("Charge successful: #{charge.id}")
%{paid: false} ->
IO.puts("Charge failed: #{charge.failure_message}")
end

ด้วย Pattern Matching

defmodule Paymentบริการ do
def create_charge(token, amount) do
case Omise.Charge.create(amount: amount, currency: "THB", card: token) do
{:ok, %{paid: true} = charge} ->
{:ok, charge}

{:ok, %{paid: false, failure_message: message}} ->
{:error, message}

{:error, error} ->
{:error, error}
end
end
end

ด้วย 3D Secure

def create_secure_charge(token, amount, return_uri) do
case Omise.Charge.create(
amount: amount,
currency: "THB",
card: token,
return_uri: return_uri
) do
{:ok, %{authorized: true, authorize_uri: uri} = charge} when not is_nil(uri) ->
{:redirect, uri}

{:ok, %{authorized: true, paid: true} = charge} ->
{:success, charge}

{:ok, %{failure_message: message}} ->
{:error, message}

{:error, error} ->
{:error, error}
end
end

การดึงค่าธรรมเนียม

case Omise.Charge.retrieve("chrg_test_123") do
{:ok, charge} ->
IO.puts("Amount: #{charge.amount}")
IO.puts("Currency: #{charge.currency}")
IO.puts("Status: #{charge.status}")
IO.puts("Paid: #{charge.paid}")

{:error, error} ->
IO.puts("Error: #{inspect(error)}")
end

การแสดงรายชื่อค่าธรรมเนียม

# แสดงรายการค่าธรรมเนียมทั้งหมดพร้อม pagination
{:ok, charges} = Omise.Charge.list(
limit: 20,
offset: 0,
order: "reverse_chronological"
)

charges.data
|> Enum.filter(&(&1.paid))
|> Enum.each(fn charge ->
IO.puts("#{charge.id}: #{charge.amount} #{charge.currency}")
end)

# แสดงรายการค่าธรรมเนียมด้วยตัวกรองวันที่
week_ago = DateTime.utc_now() |> DateTime.add(-7, :day) |> DateTime.to_date()
today = Date.utc_today()

{:ok, recent_charges} = Omise.Charge.list(
from: week_ago,
to: today
)

การสร้างลูกค้า

{:ok, customer} = Omise.Customer.create(
email: "customer@example.com",
description: "John Doe",
metadata: %{
user_id: "12345",
account_type: "premium"
}
)

IO.puts("Customer created: #{customer.id}")

การบันทึกบัตรเข้าไปในลูกค้า

# อัปเดตลูกค้าด้วย card token
{:ok, customer} = Omise.Customer.update(
"cust_test_123",
card: "tokn_test_456"
)

IO.puts("Card saved: #{customer.default_card}")

# หรือสร้างลูกค้าพร้อมบัตรในขั้นตอนเดียว
{:ok, customer} = Omise.Customer.create(
email: "customer@example.com",
description: "John Doe",
card: "tokn_test_123"
)

การแสดงรายชื่อบัตรลูกค้า

{:ok, customer} = Omise.Customer.retrieve("cust_test_123")

customer.cards.data
|> Enum.each(fn card ->
IO.puts("#{card.brand} ending in #{card.last_digits}")
IO.puts("Expires: #{card.expiration_month}/#{card.expiration_year}")
end)

การสร้างการคืนเงิน

# คืนเงินเต็มจำนวน
{:ok, refund} = Omise.Refund.create(
"chrg_test_123",
%{}
)

# คืนเงินบางส่วน
{:ok, refund} = Omise.Refund.create(
"chrg_test_123",
%{
amount: 25_000, # 250.00 THB
metadata: %{
reason: "customer_request",
ticket_id: "TICKET-123"
}
}
)

IO.puts("Refund #{refund.id}: #{refund.amount} #{refund.currency}")

การสร้างการโอน

{:ok, transfer} = Omise.Transfer.create(
amount: 500_000, # 5,000.00 THB
recipient: "recp_test_123",
metadata: %{
payout_id: "PAYOUT-456"
}
)

IO.puts("Transfer #{transfer.id}: #{transfer.amount}")

วิธีการชำระเงินแบบอื่น

การสร้างแหล่งที่มา

# QR PromptPay
{:ok, source} = Omise.Source.create(
type: "promptpay",
amount: 100_000,
currency: "THB"
)

IO.puts("QR Code URL: #{source.scannable_code.image.download_uri}")

# สร้างค่าธรรมเนียมด้วย source
{:ok, charge} = Omise.Charge.create(
amount: 100_000,
currency: "THB",
source: source.id,
return_uri: "https://example.com/payment/callback"
)

ธนาคารอินเทอร์เน็ต

{:ok, source} = Omise.Source.create(
type: "internet_banking_scb",
amount: 100_000,
currency: "THB"
)

{:ok, charge} = Omise.Charge.create(
amount: 100_000,
currency: "THB",
source: source.id,
return_uri: "https://example.com/payment/callback"
)

# เปลี่ยนเส้นทางลูกค้าไปยัง charge.authorize_uri

Mobile Banking

{:ok, source} = Omise.Source.create(
type: "mobile_banking_scb",
amount: 100_000,
currency: "THB"
)

{:ok, charge} = Omise.Charge.create(
amount: 100_000,
currency: "THB",
source: source.id,
return_uri: "https://example.com/payment/callback"
)

การผ่อนชำระ

{:ok, source} = Omise.Source.create(
type: "installment_kbank",
amount: 100_000,
currency: "THB",
installment_term: 6 # 6 เดือน
)

{:ok, charge} = Omise.Charge.create(
amount: 100_000,
currency: "THB",
source: source.id,
return_uri: "https://example.com/payment/callback"
)

การจัดการข้อผิดพลาด

defmodule Paymentบริการ do
def create_charge_with_error_handling(params) do
case Omise.Charge.create(params) do
{:ok, charge} ->
{:ok, charge}

{:error, %{code: "authentication_failure"}} ->
{:error, "Invalid API key"}

{:error, %{code: "invalid_card"}} ->
{:error, "Card was declined"}

{:error, %{code: "insufficient_fund"}} ->
{:error, "Insufficient funds"}

{:error, error} ->
{:error, "Payment failed: #{inspect(error)}"}
end
end
end

ตัวจัดการข้อผิดพลาดแบบกำหนดเอง

defmodule PaymentErrorHandler do
@error_messages %{
"insufficient_fund" => "ยอดเงินในบัตรไม่เพียงพอ",
"stolen_or_lost_card" => "บัตรถูกรายงานว่าถูกขโมยหรือสูญหาย",
"invalid_security_code" => "รหัส CVV ไม่ถูกต้อง",
"payment_cancelled" => "การชำระเงินถูกยกเลิก"
}

def get_error_message({:error, %{code: code}}), do: Map.get(@error_messages, code, "ข้อผิดพลาดที่ไม่รู้จัก")
def get_error_message({:error, error}), do: "ข้อผิดพลาด: #{inspect(error)}"
def get_error_message(_), do: "ข้อผิดพลาดที่ไม่รู้จัก"
end

การผสานรวม Phoenix

ตัวอย่างตัวควบคุม

defmodule MyAppWeb.Paymentตัวควบคุม do
use MyAppWeb, :controller

alias MyApp.Payments
alias MyApp.Orders

def create(conn, %{"order_id" => order_id, "omise_token" => token}) do
with {:ok, order} <- Orders.get_order(order_id),
{:ok, charge} <- create_charge(order, token),
{:ok, payment} <- Payments.save_payment(charge, order) do

handle_charge_result(conn, charge, order)
else
{:error, :not_found} ->
conn
|> put_flash(:error, "Order not found")
|> redirect(to: เส้นทาง.order_path(conn, :index))

{:error, reason} ->
conn
|> put_flash(:error, "Payment failed: #{reason}")
|> redirect(to: เส้นทาง.payment_path(conn, :new, order_id))
end
end

defp create_charge(order, token) do
Omise.Charge.create(
amount: round(order.total * 100),
currency: "THB",
card: token,
description: "Order ##{order.id}",
metadata: %{
order_id: order.id,
customer_email: order.email
},
return_uri: เส้นทาง.payment_url(MyAppWeb.Endpoint, :callback)
)
end

defp handle_charge_result(conn, %{paid: true} = charge, order) do
Orders.mark_paid(order)

conn
|> put_flash(:info, "Payment successful!")
|> redirect(to: เส้นทาง.order_path(conn, :show, order))
end

defp handle_charge_result(conn, %{authorize_uri: uri} = _charge, _order) when not is_nil(uri) do
redirect(conn, external: uri)
end

defp handle_charge_result(conn, %{failure_message: message}, order) do
conn
|> put_flash(:error, message)
|> redirect(to: เส้นทาง.payment_path(conn, :new, order))
end

def callback(conn, %{"id" => charge_id}) do
with {:ok, charge} <- Omise.Charge.retrieve(charge_id),
{:ok, payment} <- Payments.get_by_charge_id(charge_id),
{:ok, _payment} <- Payments.update_status(payment, charge) do

if charge.paid do
conn
|> put_flash(:info, "Payment successful!")
|> redirect(to: เส้นทาง.order_path(conn, :show, payment.order_id))
else
conn
|> put_flash(:error, charge.failure_message)
|> redirect(to: เส้นทาง.payment_path(conn, :new, payment.order_id))
end
else
{:error, _reason} ->
conn
|> put_flash(:error, "Payment verification failed")
|> redirect(to: เส้นทาง.page_path(conn, :index))
end
end
end

โมดูล Context

defmodule MyApp.Payments do
import Ecto.Query
alias MyApp.Repo
alias MyApp.Payments.Payment

def save_payment(charge, order) do
%Payment{}
|> Payment.changeset(%{
order_id: order.id,
charge_id: charge.id,
amount: Decimal.div(charge.amount, 100),
currency: charge.currency,
status: charge.status,
paid: charge.paid
})
|> Repo.insert()
end

def get_by_charge_id(charge_id) do
case Repo.get_by(Payment, charge_id: charge_id) do
nil -> {:error, :not_found}
payment -> {:ok, payment}
end
end

def update_status(payment, charge) do
payment
|> Payment.changeset(%{
status: charge.status,
paid: charge.paid,
failure_code: charge.failure_code,
failure_message: charge.failure_message
})
|> Repo.update()
end

def charge_customer(customer_id, amount) do
Omise.Charge.create(
amount: round(amount * 100),
currency: "THB",
customer: customer_id
)
end

def refund_charge(charge_id, amount \\ nil) do
params = if amount, do: %{amount: round(amount * 100)}, else: %{}
Omise.Refund.create(charge_id, params)
end
end

Schema

defmodule MyApp.Payments.Payment do
use Ecto.Schema
import Ecto.Changeset

schema "payments" do
field :order_id, :id
field :charge_id, :string
field :amount, :decimal
field :currency, :string, default: "THB"
field :status, :string
field :paid, :boolean, default: false
field :failure_code, :string
field :failure_message, :string
field :refund_id, :string
field :refund_amount, :decimal

timestamps()
end

def changeset(payment, attrs) do
payment
|> cast(attrs, [
:order_id, :charge_id, :amount, :currency, :status,
:paid, :failure_code, :failure_message, :refund_id, :refund_amount
])
|> validate_required([:order_id, :amount, :currency, :status])
|> validate_number(:amount, greater_than: 0)
|> unique_constraint(:charge_id)
end
end

GenServer สำหรับการประมวลผลเบื้องหลัง

ตัวประมวลผลการชำระเงิน

defmodule MyApp.PaymentProcessor do
use GenServer
require Logger

def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end

def process_charge(token, amount, metadata) do
GenServer.call(__MODULE__, {:process_charge, token, amount, metadata})
end

@impl true
def init(_opts) do
{:ok, %{}}
end

@impl true
def handle_call({:process_charge, token, amount, metadata}, _from, state) do
result = case Omise.Charge.create(
amount: amount,
currency: "THB",
card: token,
metadata: metadata
) do
{:ok, %{paid: true} = charge} ->
Logger.info("Charge successful: #{charge.id}")
{:ok, charge}

{:ok, %{paid: false, failure_message: message} = charge} ->
Logger.error("Charge failed: #{message}")
{:error, message}

{:error, error} ->
Logger.error("Charge error: #{inspect(error)}")
{:error, error}
end

{:reply, result, state}
end
end

ตัวจัดการสมาชิก

defmodule MyApp.SubscriptionManager do
use GenServer
require Logger

alias MyApp.Repo
alias MyApp.Subscriptions.Subscription

def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end

@impl true
def init(_opts) do
schedule_work()
{:ok, %{}}
end

@impl true
def handle_info(:charge_subscriptions, state) do
charge_all_subscriptions()
schedule_work()
{:noreply, state}
end

defp schedule_work do
# กำหนดเวลาสำหรับวันถัดไปเวลาเที่ยงคืน
next_run = calculate_next_midnight()
Process.send_after(self(), :charge_subscriptions, next_run)
end

defp charge_all_subscriptions do
Subscription
|> where([s], s.status == "active")
|> Repo.all()
|> Enum.each(&charge_subscription/1)
end

defp charge_subscription(subscription) do
case Omise.Charge.create(
amount: subscription.plan_amount,
currency: "THB",
customer: subscription.customer_id,
description: "Subscription #{Date.utc_today()}"
) do
{:ok, %{paid: true}} ->
subscription
|> Ecto.Changeset.change(last_charge_date: DateTime.utc_now())
|> Repo.update()

Logger.info("Charged subscription #{subscription.id}")

{:error, error} ->
Logger.error("Failed to charge subscription #{subscription.id}: #{inspect(error)}")
end
end

defp calculate_next_midnight do
now = DateTime.utc_now()
tomorrow = DateTime.add(now, 1, :day)
midnight = %{tomorrow | hour: 0, minute: 0, second: 0, microsecond: {0, 0}}
DateTime.diff(midnight, now, :millisecond)
end
end

Task สำหรับการดำเนินการแบบ Async

defmodule MyApp.AsyncPayment do
def process_charges_async(charge_params_list) do
charge_params_list
|> Enum.map(fn params ->
Task.async(fn -> create_charge(params) end)
end)
|> Enum.map(&Task.await/1)
end

defp create_charge(params) do
case Omise.Charge.create(params) do
{:ok, charge} -> {:ok, charge}
{:error, error} -> {:error, error}
end
end
end

# การใช้
charge_params = [
%{amount: 100_000, currency: "THB", card: "tokn_test_1"},
%{amount: 200_000, currency: "THB", card: "tokn_test_2"}
]

results = MyApp.AsyncPayment.process_charges_async(charge_params)

แนวทางปฏิบัติที่ดีที่สุด

1. ใช้ Pattern Matching

def process_payment(token, amount) do
case Omise.Charge.create(amount: amount, currency: "THB", card: token) do
{:ok, %{paid: true, id: charge_id}} ->
# จัดการการชำระเงินที่สำเร็จ
{:ok, charge_id}

{:ok, %{authorize_uri: uri}} when not is_nil(uri) ->
# จัดการการเปลี่ยนเส้นทาง 3D Secure
{:redirect, uri}

{:ok, %{failure_message: message}} ->
# จัดการการชำระเงินที่ล้มเหลว
{:error, message}

{:error, %{code: code, message: message}} ->
# จัดการข้อผิดพลาด API
{:error, "#{code}: #{message}"}
end
end

2. ใช้ Pipes สำหรับการแปลงข้อมูล

def list_paid_charges do
{:ok, charges} = Omise.Charge.list(limit: 100)

charges.data
|> Enum.filter(&(&1.paid))
|> Enum.map(&extract_charge_info/1)
|> Enum.sort_by(& &1.created, :desc)
end

defp extract_charge_info(charge) do
%{
id: charge.id,
amount: charge.amount / 100,
currency: charge.currency,
created: charge.created
}
end

3. ใช้ Structs เพื่อความปลอดภัยของประเภทข้อมูล

defmodule MyApp.Money do
defstruct amount: 0, currency: "THB"

def new(amount, currency \\ "THB") do
%__MODULE__{amount: round(amount * 100), currency: currency}
end

def to_baht(%__MODULE__{amount: amount}), do: amount / 100
end

# การใช้
money = MyApp.Money.new(1000.00)
{:ok, charge} = Omise.Charge.create(
amount: money.amount,
currency: money.currency,
card: token
)

4. ใช้ Idempotency

def create_idempotent_charge(params, order_id) do
idempotency_key = "order-#{order_id}-#{:os.system_time(:millisecond)}"

Omise.Charge.create(
params,
headers: [{"Idempotency-Key", idempotency_key}]
)
end

5. ใช้ Supervisors เพื่อความทนทานต่อข้อผิดพลาด

defmodule MyApp.Application do
use Application

def start(_type, _args) do
children = [
MyApp.Repo,
MyAppWeb.Endpoint,
MyApp.PaymentProcessor,
MyApp.SubscriptionManager
]

opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end

6. ใช้ Telemetry สำหรับการติดตาม

defmodule MyApp.PaymentTelemetry do
def handle_event([:omise, :charge, :create], measurements, metadata, _config) do
Logger.info("Charge created",
charge_id: metadata.charge_id,
amount: metadata.amount,
duration: measurements.duration
)
end
end

# แนบ handler
:telemetry.attach(
"payment-handler",
[:omise, :charge, :create],
&MyApp.PaymentTelemetry.handle_event/4,
nil
)

การทดสอบ

การทดสอบ ExUnit

defmodule MyApp.PaymentsTest do
use MyApp.DataCase

alias MyApp.Payments

import Mox

setup :verify_on_exit!

describe "create_charge/2" do
test "สร้างค่าธรรมเนียมที่สำเร็จ" do
expect(OmiseMock, :create_charge, fn _params ->
{:ok, %{
id: "chrg_test_123",
amount: 100_000,
currency: "THB",
paid: true,
status: "successful"
}}
end)

assert {:ok, charge} = Payments.create_charge("tokn_test_123", 1000.00)
assert charge.id == "chrg_test_123"
assert charge.paid == true
end

test "จัดการความล้มเหลวของค่าธรรมเนียม" do
expect(OmiseMock, :create_charge, fn _params ->
{:error, %{code: "insufficient_fund", message: "Insufficient funds"}}
end)

assert {:error, error} = Payments.create_charge("tokn_test_123", 1000.00)
assert error.code == "insufficient_fund"
end
end
end

การทดสอบการผสานรวม

defmodule MyAppWeb.PaymentตัวควบคุมTest do
use MyAppWeb.ConnCase

alias MyApp.Orders

setup do
order = Orders.create_order!(%{total: 1000.00, email: "test@example.com"})
{:ok, order: order}
end

describe "POST /payment/:order_id" do
@tag :integration
test "สร้างการชำระเงินสำเร็จ", %{conn: conn, order: order} do
# ใช้ test token
token = "tokn_test_5086xl7ddjbases4sq3i"

conn = post(conn, เส้นทาง.payment_path(conn, :create, order.id), %{
"omise_token" => token
})

assert redirected_to(conn) == เส้นทาง.order_path(conn, :show, order)
assert get_flash(conn, :info) == "Payment successful!"
end
end
end

Mock สำหรับการทดสอบ

# test/support/mocks.ex
Mox.defmock(OmiseMock, for: MyApp.PaymentBehaviour)

# lib/my_app/payment_behaviour.ex
defmodule MyApp.PaymentBehaviour do
@callback create_charge(map()) :: {:ok, map()} | {:error, map()}
@callback retrieve_charge(String.t()) :: {:ok, map()} | {:error, map()}
end

# ใช้ในแอปพลิเคชัน
defmodule MyApp.Payments do
@payment_client Application.compile_env(:my_app, :payment_client, Omise)

def create_charge(params) do
@payment_client.Charge.create(params)
end
end

# ใน test config
config :my_app, payment_client: OmiseMock

การแก้ไขปัญหา

การหมดเวลาการเชื่อมต่อ

# กำหนดค่าการหมดเวลาของ HTTP client
config :omise,
http_options: [timeout: 60_000, recv_timeout: 60_000]

การบันทึก Debug

# เปิดใช้งานการบันทึก debug
config :logger, level: :debug

config :omise, debug: true

การตรวจสอบลายเซ็นเว็บฮุก

defmodule MyAppWeb.Webhookตัวควบคุม do
use MyAppWeb, :controller

def omise(conn, _params) do
with {:ok, payload} <- read_body(conn),
{:ok, signature} <- get_signature(conn),
true <- verify_signature(payload, signature) do

payload
|> Jason.decode!()
|> handle_webhook()

json(conn, %{status: "ok"})
else
false ->
conn
|> put_status(:unauthorized)
|> json(%{error: "Invalid signature"})

{:error, _reason} ->
conn
|> put_status(:bad_request)
|> json(%{error: "Invalid request"})
end
end

defp get_signature(conn) do
case get_req_header(conn, "omise-signature") do
[signature] -> {:ok, signature}
_ -> {:error, :no_signature}
end
end

defp verify_signature(payload, signature) do
secret = Application.get_env(:omise, :webhook_secret)
expected = :crypto.mac(:hmac, :sha256, secret, payload) |> Base.encode16(case: :lower)
Plug.Crypto.secure_compare(signature, expected)
end

defp handle_webhook(%{"key" => "charge.complete", "data" => data}) do
# จัดการการชำระเงินที่เสร็จสมบูรณ์
end

defp handle_webhook(%{"key" => "refund.create", "data" => data}) do
# จัดการการสร้างการคืนเงิน
end

defp handle_webhook(_), do: :ok
end

คำถามที่พบบ่อย

ฉันจะจัดการ webhooks ใน Phoenix ได้อย่างไร?

defmodule MyAppWeb.Webhookตัวควบคุม do
use MyAppWeb, :controller

def omise(conn, _params) do
{:ok, body, conn} = read_body(conn)
event = Jason.decode!(body)

case event["key"] do
"charge.complete" ->
handle_charge_complete(event["data"])
"refund.create" ->
handle_refund_create(event["data"])
end

json(conn, %{status: "ok"})
end

defp handle_charge_complete(%{"id" => charge_id}) do
{:ok, charge} = Omise.Charge.retrieve(charge_id)
MyApp.Payments.update_charge_status(charge)
end
end

ฉันจะทดสอบโดยไม่มีการเรียกเก็บเงินจริงได้อย่างไร?

# ใช้ test API keys ใน config/test.exs
config :omise,
public_key: "pkey_test_123456789",
secret_key: "skey_test_123456789"

# ใช้ test tokens
{:ok, charge} = Omise.Charge.create(
amount: 100_000,
currency: "THB",
card: "tokn_test_5086xl7ddjbases4sq3i"
)

# ตรวจสอบโหมดทดสอบ
assert charge.livemode == false

ฉันจะจัดการการคืนเงินบางส่วนได้อย่างไร?

def refund_charge(charge_id, refund_amount \\ nil) do
with {:ok, charge} <- Omise.Charge.retrieve(charge_id) do
refundable = charge.amount - charge.refunded

params = case refund_amount do
nil -> %{}
amount when amount <= refundable -> %{amount: round(amount * 100)}
_ -> {:error, :amount_exceeds_refundable}
end

case params do
{:error, reason} -> {:error, reason}
_ -> Omise.Refund.create(charge_id, params)
end
end
end

ฉันจะบันทึกบัตรหลายใบสำหรับลูกค้าได้อย่างไร?

def add_card_to_customer(customer_id, token) do
Omise.Customer.update(customer_id, card: token)
end

def list_customer_cards(customer_id) do
with {:ok, customer} <- Omise.Customer.retrieve(customer_id) do
{:ok, customer.cards.data}
end
end

def charge_specific_card(customer_id, card_id, amount) do
Omise.Charge.create(
amount: round(amount * 100),
currency: "THB",
customer: customer_id,
card: card_id
)
end

ฉันจะใช้การเรียกเก็บเงินแบบสมาชิกได้อย่างไร?

defmodule MyApp.SubscriptionBilling do
def charge_monthly(customer_id, plan_amount) do
Omise.Charge.create(
amount: plan_amount,
currency: "THB",
customer: customer_id,
description: "Subscription #{Date.utc_today()}"
)
end
end

# ใช้กับ Quantum สำหรับการกำหนดเวลา
config :my_app, MyApp.Scheduler,
jobs: [
{"0 0 1 * *", {MyApp.SubscriptionBilling, :charge_all, []}}
]

ฉันจะใช้ตรรกะการลองใหม่ได้อย่างไร?

def create_charge_with_retry(params, retries \\ 3) do
case Omise.Charge.create(params) do
{:ok, charge} ->
{:ok, charge}

{:error, _error} when retries > 0 ->
:timer.sleep(:math.pow(2, 3 - retries) * 1000 |> round())
create_charge_with_retry(params, retries - 1)

{:error, error} ->
{:error, error}
end
end

ทรัพยากรที่เกี่ยวข้อง

ขั้นตอนถัดไป

การสนับสนุน

หากคุณพบปัญหากับไลบรารี Elixir:

  1. ตรวจสอบ GitHub Issues
  2. ตรวจสอบ เอกสาร API
  3. ติดต่อ support@omise.co พร้อมด้วย:
    • เวอร์ชัน Elixir
    • เวอร์ชันไลบรารี omise-elixir
    • ข้อความข้อผิดพลาดและ stack trace
    • ขั้นตอนในการทำซ้ำ