Skip to content
Hayato Tomoda edited this page Jun 24, 2026 · 30 revisions

Version 2.3.0

New Features

USER_ACCOUNT_DELETED Webhook Event

The SDK can now parse EVENT_TYPE_USER_ACCOUNT_DELETED webhooks, which are fired when a Jamm user account is deleted. Previously, these payloads raised Unknown event type.

message = Jamm::Webhook.parse(json_hash)

case message.event_type
when Jamm::OpenAPI::EventType::USER_ACCOUNT_DELETED     👈️👈️👈️
  account = message.content
  puts account.customer       # "cus-..."
  puts account.email
  puts account.deleted_at
  puts account.merchant_name
end

Flat refund_id on Refund Webhooks

Refund webhook payloads now surface the refund's rfd- identifier on both the flat refund_id attribute and the nested refund.id, so either path works:

when Jamm::OpenAPI::EventType::REFUND_SUCCEEDED
  charge = message.content
  puts charge.refund_id       # "rfd-..."   👈️ flat attribute
  puts charge.refund.id       # "rfd-..."   👈️ nested attribute

Fixed

Forward-Compatible Webhook Parsing

Webhook parsing no longer fails when the Jamm backend adds new fields to a payload. Unknown keys are silently dropped instead of raising ArgumentError, so SDK upgrades are no longer required to keep ingesting webhooks after a backend release.

Numeric Enum Wire Values Resolved on Every Webhook Model

The Jamm backend serializes webhooks with Go's json.Marshal (not protojson), so every enum field (status, api_source, ...) arrives on the wire as an integer. The SDK now maps every numeric enum back to its string constant on every webhook model, matching the values returned by the REST API:

when Jamm::OpenAPI::EventType::REFUND_SUCCEEDED
  charge = message.content
  charge.status          # "STATUS_REFUNDED" (was: 5)            👈️👈️👈️
  charge.api_source      # "API_SOURCE_OFF_SESSION" (was: 2)     👈️👈️👈️

Nested Webhook Fields Are Typed Model Instances

Nested webhook fields (e.g. refund, refund.error) are now coerced into typed model instances instead of staying as raw Hash values. Typed accessors like error.code / error.message work as documented instead of raising NoMethodError:

when Jamm::OpenAPI::EventType::REFUND_FAILED
  refund = message.content.refund        # Jamm::OpenAPI::RefundInfo   👈️ typed
  error = refund.error                   # Jamm::OpenAPI::ChargeError  👈️ typed
  puts error.code                        # works (was: NoMethodError)
  puts error.message

Webhook.parse Accepts String or Symbol Keys

Jamm::Webhook.parse now accepts payloads with either string keys (JSON.parse(body)) or symbol keys (JSON.parse(body, symbolize_names: true)). Both decode paths are normalized internally, so callers can pick whichever they prefer:

# Both work in 2.3.0
Jamm::Webhook.parse(JSON.parse(body))
Jamm::Webhook.parse(JSON.parse(body, symbolize_names: true))

Version 2.2.0

New Features

Async Off-Session Payment

A new Jamm::Payment.off_session_async method has been added for async off-session charges. Unlike the synchronous off_session, the API returns immediately with a request_id and the final charge result is delivered later via webhook (CHARGE_SUCCESS / CHARGE_FAIL).

response = Jamm::Payment.off_session_async(
    customer: 'cus-12345',
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
    ),
)

response.request_id     # request identifier for tracking
response.status         # Jamm::OpenAPI::AsyncStatus (e.g. "ASYNC_STATUS_PENDING")
response.charge_id      # populated once the charge has been created

# Platform mode
Jamm::Payment.off_session_async(
    merchant: 'mer-12345',
    customer: 'cus-12345',
    charge: charge,
)

Auto-Filled idempotency_key

off_session_async auto-fills idempotency_key with a UUID when the caller passes nil or a blank string, so every async charge is retry-safe by default. A caller-supplied key is left untouched so explicit retries reuse the same value:

# Auto-generated UUID (default)
Jamm::Payment.off_session_async(customer: 'cus-12345', charge: charge)

# Caller-supplied key — preserved as-is for safe retries
Jamm::Payment.off_session_async(
    customer: 'cus-12345',
    charge: charge,
    idempotency_key: 'order-2026-0042',     👈️👈️👈️
)

Version 2.1.0

New Features

Granular Refund Webhook Events

Refund outcomes are now delivered via two separate webhook events, replacing the single CHARGE_REFUND event:

  • EVENT_TYPE_REFUND_SUCCEEDED — fired when a refund completes successfully
  • EVENT_TYPE_REFUND_FAILED — fired when a refund fails
message = Jamm::Webhook.parse(json_hash)

case message.event_type
when Jamm::OpenAPI::EventType::REFUND_SUCCEEDED     👈️👈️👈️
  refund = message.refund
  puts refund.refund_id         # "rfd-..."
  puts refund.amount_refunded
  puts refund.jamm_fee
  puts refund.consumption_tax   # 10% of Jamm fee
  puts refund.original_transaction_fee_waived  # true for same-day cancel
  puts refund.processed_at

when Jamm::OpenAPI::EventType::REFUND_FAILED         👈️👈️👈️
  error = message.refund&.error
  puts error.code               # Jamm error code
  puts error.message            # human-readable description
end

New RefundInfo Model

Refund webhook payloads now include a nested refund object (Jamm::OpenAPI::RefundInfo) with detailed refund information:

Field Type Description
refund_id String Refund identifier (rfd-* format)
amount_refunded Integer Refund amount
jamm_fee Integer Jamm fee charged for this refund
consumption_tax Integer 10% of Jamm fee
original_transaction_fee_waived Boolean true for same-day cancellation
error ChargeError Error details (on failure only)
processed_at String Processing timestamp

New ChargeError Model

A new Jamm::OpenAPI::ChargeError model provides structured error details for failed refunds:

error = message.refund&.error
error.code      # Jamm error code string
error.message   # human-readable description

New ChargeMessage Fields

The webhook ChargeMessage payload now includes additional fields for refund events:

Field Type Description
jamm_fee Integer Jamm fee for this refund
consumption_tax Integer 10% of Jamm fee
original_transaction_jamm_fee String "waived" or "not_waived"
refund_id String External refund identifier (rfd-*)
refund RefundInfo Nested refund details object

New Charge Status: REFUNDED

A new ChargeMessageStatus::REFUNDED (STATUS_REFUNDED) value has been added to indicate that a charge has been fully refunded.

Breaking Changes

Webhook event type renamed: CHARGE_REFUNDREFUND_SUCCEEDED / REFUND_FAILED

The CHARGE_REFUND event type has been removed and replaced with two granular events. Update your webhook handler:

# BEFORE (2.0.0)
when Jamm::OpenAPI::EventType::CHARGE_REFUND

# AFTER (2.1.0)
when Jamm::OpenAPI::EventType::REFUND_SUCCEEDED     👈️👈️👈️
  # handle successful refund
when Jamm::OpenAPI::EventType::REFUND_FAILED         👈️👈️👈️
  # handle failed refund

Version 2.0.0

New Features

Platform Mode

The SDK now supports Platform Mode, allowing platformers to call the Jamm API on behalf of their merchants.

Configure platform mode during initialization:

Jamm.configure(
    client_id: '<your client id>',
    client_secret: '<your client secret>',
    env: 'production',
    platform: true,                         👈️👈️👈️
)

Once configured, all API methods accept an optional merchant: parameter to specify which merchant to act on behalf of:

# Payment on behalf of a merchant
Jamm::Payment.on_session(
    merchant: 'mer-12345',                  👈️👈️👈️
    buyer: Jamm::OpenAPI::Buyer.new(
        email: 'foo@example.com',
        name: 'Yamada Taro',
    ),
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
    ),
    redirect: Jamm::OpenAPI::URL.new(
        success_url: 'https://jamm-pay.jp/success',
        failure_url: 'https://jamm-pay.jp/fail',
    )
)

# Customer management on behalf of a merchant
Jamm::Customer.get('cus-12345', merchant: 'mer-12345')
Jamm::Customer.create(buyer: buyer, merchant: 'mer-12345')
Jamm::Customer.update('cus-12345', params, merchant: 'mer-12345')
Jamm::Customer.delete('cus-12345', merchant: 'mer-12345')

The merchant: parameter is available on all modules: Payment, Customer, Healthcheck.

Refund Support

A new Jamm::Payment.refund method is now available. If the same-day cancellation window has not passed, the charge is cancelled directly. Otherwise, a bank transfer refund request is created. The final result is delivered via webhook (charge_refund).

request = Jamm::OpenAPI::RefundRequest.new(
    charge: 'chg-12345',
)

Jamm::Payment.refund(request)

# Platform mode
Jamm::Payment.refund(request, merchant: 'mer-12345')

Charge Status Enum

A new ChargeStatus enum has been added with the following values:

  • CHARGE_STATUS_SUCCESS
  • CHARGE_STATUS_FAILURE
  • CHARGE_STATUS_WAITING_EKYC
  • CHARGE_STATUS_BLOCKING
  • CHARGE_STATUS_CANCELLED
  • CHARGE_STATUS_REFUNDED
  • CHARGE_STATUS_PENDING

Breaking Changes

Jamm.configure signature change

Jamm.configure now accepts an optional platform: keyword argument. Existing calls without platform: will continue to work (defaults to false), but the SDK now sets Jamm.mode internally. This is not a breaking change for existing merchant integrations.

Webhook event type renamed: CHARGE_CANCELCHARGE_REFUND

The CHARGE_CANCEL event type has been renamed to CHARGE_REFUND. If you are matching on webhook event types, update your code:

# BEFORE (1.x)
when Jamm::OpenAPI::EventType::CHARGE_CANCEL

# AFTER (2.0.0)
when Jamm::OpenAPI::EventType::CHARGE_REFUND        👈️👈️👈️

Deleted Features

Jamm::Charge module removed

The Jamm::Charge module, deprecated since v1.4.0, has been permanently removed. All deprecated methods are no longer available:

  • Jamm::Charge.create_with_redirect → use Jamm::Payment.on_session
  • Jamm::Charge.create_without_redirect → use Jamm::Payment.off_session
  • Jamm::Charge.get → use Jamm::Payment.get
  • Jamm::Charge.list → use Jamm::Payment.list

Version 1.7.0

New Features

On Session Payment now supports one-time payment mode.

Jamm::Payment.on_session(
    one_time: true,                     👈️👈️👈️
    buyer: Jamm::OpenAPI::Buyer.new(
        email: 'foo@example.com',
        name: 'Yamada Taro',
        force_kyc: true,
        katakana_last_name: 'ヤマダ',
        katakana_first_name: 'タロウ',
        address: 'Tokyo, Minato-ku 1-2-3',
        birth_date: '2000-01-23',
        gender: 'male',
        metadata: {},
    ),
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    ),
    redirect: Jamm::OpenAPI::URL.new(
        success_url: 'https://jamm-pay.jp/success',
        failure_url: 'https://jamm-pay.jp/fail',
    )
)

Version 1.6.0

Improvements

customer.update() methods now supports updating customer (buyer) email and phone number.

Jamm::Customer.update('cus-12345', {
    email: 'new-email-address@example.com',
    phone: '09012345678',
})

Please check out Customer Update Parameters for more information.

Version 1.5.0

New Features

Added a new link_initiated field to the Jamm::Customer.get response that indicates whether a payment link associated with this customer has been clicked or initialized.

Please check Customer object for more information.

Minor Changes

Optional Redirect Links

Redirect links are now optional. If a redirect link is provided, the user will be redirected, otherwise they will remain on the same page.

# On Session Payment with redirect object.
Jamm::Payment.on_session(
    buyer: Jamm::OpenAPI::Buyer.new(
        email: 'foo@example.com',
        name: 'Yamada Taro',
        force_kyc: true,
        katakana_last_name: 'ヤマダ',
        katakana_first_name: 'タロウ',
        address: 'Tokyo, Minato-ku 1-2-3',
        birth_date: '2000-01-23',
        gender: 'male',
        metadata: {},
    ),
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    ),
    # Only set this object when merchant wants to redirect
    # user back after completing payment approval.
    redirect: Jamm::OpenAPI::URL.new(               👈️👈️👈️
        success_url: 'https://jamm-pay.jp/success',
        failure_url: 'https://jamm-pay.jp/fail',
    )
)

# On Session Payment without redirect object.
Jamm::Payment.on_session(
    buyer: Jamm::OpenAPI::Buyer.new(
        email: 'foo@example.com',
        name: 'Yamada Taro',
        force_kyc: true,
        katakana_last_name: 'ヤマダ',
        katakana_first_name: 'タロウ',
        address: 'Tokyo, Minato-ku 1-2-3',
        birth_date: '2000-01-23',
        gender: 'male',
        metadata: {},
    ),
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    ),
    # Omit redirect object when merchant does not need to
    # bring user back to website after payment approval.
)

Version 1.4.0

We are deprecating some methods from older versions. You will see deprecation warning when those methods are called, and we will remove them entirely from our backend in future versions. Once it's removed from our backend, all method calls will return HTTP error.

Please follow our migration guide in below to transition to our latest methods.

Deprecating Methods

  • Jamm::Charge::create_with_redirect
  • Jamm::Charge::create_without_redirect
  • Jamm::Charge::get
  • Jamm::Charge::list

Migration Guide

Jamm::Charge::create_with_redirect

This method was supposed to provide Payment Link to bring customer to Jamm application to confirm and approve their payment. Ruby SDK 1.4.0 start to provide such functionality through Jamm::Payment.on_session method.

If you are creating Payment Link for new customer, you should have buyer object attached. The input structure is exactly the same, just swap the method call.

# DEPRECATING
Jamm::Charge.create_with_redirect(
    buyer: Jamm::OpenAPI::Buyer.new(
        email: 'foo@example.com',
        name: 'Yamada Taro',
        force_kyc: true,
        katakana_last_name: 'ヤマダ',
        katakana_first_name: 'タロウ',
        address: 'Tokyo, Minato-ku 1-2-3',
        birth_date: '2000-01-23',
        gender: 'male',
        metadata: {},
    ),
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    ),
    redirect: Jamm::OpenAPI::URL.new(
        success_url: 'https://jamm-pay.jp/success',
        failure_url: 'https://jamm-pay.jp/fail'
    )
)

# NEW
Jamm::Payment.on_session(               👈️👈️👈️
    buyer: Jamm::OpenAPI::Buyer.new(
        email: 'foo@example.com',
        name: 'Yamada Taro',
        force_kyc: true,
        katakana_last_name: 'ヤマダ',
        katakana_first_name: 'タロウ',
        address: 'Tokyo, Minato-ku 1-2-3',
        birth_date: '2000-01-23',
        gender: 'male',
        metadata: {},
    ),
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    ),
    redirect: Jamm::OpenAPI::URL.new(
        success_url: 'https://jamm-pay.jp/success',
        failure_url: 'https://jamm-pay.jp/fail',
    )
)

If you are creating Payment Link for the existing customer, you should have customer object attached. The input structure is exactly the same, just swap the method call.

# DEPRECATING
Jamm::Charge.create_with_redirect(
    customer: 'cus-12345',
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    ),
    redirect: Jamm::OpenAPI::URL.new(
        success_url: 'https://jamm-pay.jp/success',
        failure_url: 'https://jamm-pay.jp/fail',
    )
)

# NEW
Jamm::Payment.on_session(               👈️👈️👈️
    customer: 'cus-12345',
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    ),
    redirect: Jamm::OpenAPI::URL.new(
        success_url: 'https://jamm-pay.jp/success',
        failure_url: 'https://jamm-pay.jp/fail'
    )
)

Jamm::Charge::create_without_redirect

This method was supposed to immediately withdraw money from the existing customer's bank account.

Ruby SDK 1.4.0 start to provide such functionality through Jamm::Payment.off_session method.

# DEPRECATING
Jamm::Charge.create_without_redirect(
    customer: 'cus-12345',
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    )
)

# NEW
Jamm::Payment.off_session(               👈️👈️👈️
    customer: 'cus-12345',
    charge: Jamm::OpenAPI::InitialCharge.new(
        price: 1000,
        description: 'Test payment',
        metadata: {},
    )
)

Jamm::Charge::get

# DEPRECATING
Jamm::Charge.get('chg-12345')

# NEW
Jamm::Payment.get('chg-12345')           👈️👈️👈️

Jamm::Charge::list

# DEPRECATING
Jamm::Charge.list(
  customer: 'cus-12345',
)

# NEW
Jamm::Payment.list(                       👈️👈️👈️
  customer: 'cus-12345',
)