Current section

Files

Jump to
yookassa lib yookassa.ex
Raw

lib/yookassa.ex

defmodule Yookassa do
@moduledoc """
Main module for working with YooKassa API.
This module provides high-level functions for creating payments, handling captures,
cancellations, and refunds through the YooKassa payment processing service.
It serves as the primary interface for integrating YooKassa payments into your application.
## Configuration
Before using this module, ensure you have configured the following in your `config.exs`:
config :yookassa,
api_url: "https://api.yookassa.ru/v3",
shop_id: "your_shop_id",
secret_key: "your_secret_key"
## Usage
The module provides functions for the complete payment lifecycle:
- Create payments with `create_payment/5`
- Capture authorized payments with `capture_payment/2`
- Cancel payments with `cancel_payment/1`
- Create refunds with `create_refund/3`
- Retrieve payment/refund info with `get_payment_info/1` and `get_refund_info/1`
## Error Handling
All functions return `{:ok, result}` on success or `{:error, details}` on failure.
Error details include HTTP status codes and error messages from the YooKassa API.
"""
alias Yookassa.Client
@doc """
Creates a new payment with explicitly specified parameters.
## Parameters
- `value`: Payment amount as a string (e.g., "100.00") or number.
- `currency`: Three-letter currency code (e.g., "RUB").
- `return_url`: URL where the user will return after payment.
- `description`: Order description that the user will see.
- `opts`: List of optional parameters, such as `capture: true`, `metadata: %{...}`.
## Example call
Yookassa.create_payment("199.50", "RUB", "https://example.com/thanks", "Order №72", capture: true)
"""
def create_payment(value, currency, return_url, description, opts \\ []) do
# 1. Assemble the basic request structure from required arguments
base_params = %{
"amount" => %{
# to_string for universality
"value" => to_string(value),
"currency" => currency
},
"confirmation" => %{
"type" => "redirect",
"return_url" => return_url
},
"description" => description,
"capture" => true
}
# 2. Convert the options list to a map and add to base parameters.
# Map.new() converts [capture: true] to %{"capture" => true}
# Map.merge() overlays options on top of base parameters.
# If opts contains "description", it will overwrite the base one.
final_params = Map.merge(base_params, Map.new(opts))
# 3. Call our client with the final set of parameters
with {:ok, response} <- Client.post("/payments", final_params),
%Req.Response{status: 200, body: body} <- response do
{:ok, body}
else
{:error, %Req.Response{status: status, body: body}} ->
{:error, %{status: status, reason: body}}
error ->
{:error, %{reason: "Unknown error", details: error}}
end
end
@doc """
Confirms the capture of a previously authorized amount (for two-stage payments).
If the payment was created with `capture: false`, this function initiates the actual
debiting of money from the user's account.
## Parameters
- `payment_id`: Payment ID in `waiting_for_capture` status.
- `opts`: Optional options. You can pass `amount: %{...}` for partial capture.
If no options are passed, the full payment amount is captured.
## Example
# Capture the full amount
Yookassa.capture_payment("21740069-...")
# Capture a partial amount
amount_to_capture = %{"value" => "50.00", "currency" => "RUB"}
Yookassa.capture_payment("21740069-...", amount: amount_to_capture)
"""
def capture_payment(payment_id, opts \\ []) do
# YooKassa API requires passing a request body, even if it's empty
body = Keyword.get(opts, :amount, %{})
with {:ok, response} <- Client.post("/payments/#{payment_id}/capture", body),
%Req.Response{status: 200, body: body} <- response do
{:ok, body}
else
{:error, %Req.Response{status: status, body: body}} ->
{:error, %{status: status, reason: body}}
error ->
{:error, %{reason: "Unknown error", details: error}}
end
end
@doc """
Cancels a payment waiting for capture (in `waiting_for_capture` status).
Funds blocked on the user's card will be unblocked.
This is not a refund, as no capture has occurred yet.
## Parameters
- `payment_id`: Payment ID in `waiting_for_capture` status.
"""
def cancel_payment(payment_id) do
# Request body for cancellation is always empty
with {:ok, response} <- Client.post("/payments/#{payment_id}/cancel", %{}),
%Req.Response{status: 200, body: body} <- response do
{:ok, body}
else
{:error, %Req.Response{status: status, body: body}} ->
{:error, %{status: status, reason: body}}
error ->
{:error, %{reason: "Unknown error", details: error}}
end
end
@doc """
Gets information about a specific payment by its ID.
Returns a Payment struct.
"""
def get_payment_info(payment_id) do
# We use Client, but now for a GET request
with {:ok, response} <- Yookassa.Client.get("/payments/#{payment_id}"),
%Req.Response{status: 200, body: body} <- response do
{:ok, Yookassa.Payment.from_map(body)}
else
{:error, %Req.Response{status: status, body: body}} ->
{:error, %{status: status, reason: body}}
error ->
{:error, %{reason: "Unknown error", details: error}}
end
end
@doc """
Creates a payment refund.
## Parameters
- `payment_id`: Payment ID for which the refund is being created.
- `value`: Refund amount as a string (e.g., "100.00") or number.
- `currency`: Three-letter currency code (e.g., "RUB").
## Example call
Yookassa.create_refund("21740069-000f-50be-b000-0486ffbf45b0", "2.00", "RUB")
"""
def create_refund(payment_id, value, currency) do
params = %{
"amount" => %{
"value" => to_string(value),
"currency" => currency
},
"payment_id" => payment_id
}
with {:ok, response} <- Client.post("/refunds", params),
%Req.Response{status: 200, body: body} <- response do
{:ok, body}
else
{:error, %Req.Response{status: status, body: body}} ->
{:error, %{status: status, reason: body}}
error ->
{:error, %{reason: "Unknown error", details: error}}
end
end
@doc """
Gets information about a specific refund by its ID.
Returns a Refund struct.
"""
def get_refund_info(refund_id) do
with {:ok, response} <- Yookassa.Client.get("/refunds/#{refund_id}"),
%Req.Response{status: 200, body: body} <- response do
{:ok, Yookassa.Refund.from_map(body)}
else
{:error, %Req.Response{status: status, body: body}} ->
{:error, %{status: status, reason: body}}
error ->
{:error, %{reason: "Unknown error", details: error}}
end
end
end