Entegrasyonlar
Ruby

Ruby SDK

lunixi-sdk, Lunixi için sunucu tarafı Ruby istemcisidir. Ed25519 imzayı OpenSSL ile üretir; token’ı eşzamanlı istekler arasında saklar, kompakt JavaScript uyumlu JSON oluşturur, güvenli tekrar denemeleri ve özgün baytlarla webhook doğrulamayı sağlar. Rails, Sinatra, Rack ve düz Ruby ile çalışır. Ana gem çerçeveden bağımsızdır; kaynak depodaki çalıştırılabilir Rails örneği ayrı adaptördür.

Gereksinimler ve kaynak kurulum

Ruby 3.3+, Bundler ve Ed25519 destekli OpenSSL gerekir. Rails örneği Rails 8.1 kullanır.

0.1.2 kaynak kod üzerinden dağıtılır. RubyGems yayını ayrı bir sürüm adımıdır; aşağıdaki kurulum yayımlanmış bir registry paketi varsaymaz.

git clone --branch v0.1.2 --depth 1 https://github.com/Lunixi-Payment/lunixi-ruby.git
cd lunixi-ruby
bundle install
gem build lunixi-sdk.gemspec
gem install ./lunixi-sdk-0.1.2.gem

Kaynak: yayımlanmış v0.1.2 etiketi (opens in a new tab). İncelenmiş yerel kopyayı gem 'lunixi-sdk', path: '/tam/yol/lunixi-ruby' ile veya Gemfile içinde tam etiketle kullanabilirsiniz:

gem 'lunixi-sdk', git: 'https://github.com/Lunixi-Payment/lunixi-ruby.git', tag: 'v0.1.2'

Bundler’ın incelenen kaynak commit’ini koruması için Gemfile.lock dosyasını saklayın. Bu, GitHub kaynak kurulumudur; RubyGems registry yayını değildir.

İstemci ve anahtarlar

require 'lunixi'
 
lunixi = Lunixi::Client.new(
  base_url: ENV.fetch('LUNIXI_BASE_URL'),
  key_id: ENV.fetch('LUNIXI_KEY_ID'),
  private_key: File.read(ENV.fetch('LUNIXI_PRIVATE_KEY_PATH')),
  timeout: 30,
  max_retries: 2
)

base_url, https://api-gateway.lunixi.com gibi yalnız HTTPS origin olmalıdır; kullanıcı bilgisi, path, query veya fragment içeremez. TEST/LIVE ortamını anahtar belirler. Her üye işyeri bağlantısı için tek istemci oluşturup paylaşın; token önbelleği thread-safe çalışır.

Sunucuda anahtar üretimi:

umask 077
openssl genpkey -algorithm ED25519 -out lunixi-private.pem
openssl pkey -in lunixi-private.pem -pubout -out lunixi-public.pem

Public anahtarı Lunixi paneline kaydedip verilen key id’yi kullanın. Özel anahtar kaynak kontrolüne ve public web dizinine girmemelidir. PKCS#8 PEM, base64 PKCS#8 DER, base64 32 bayt seed ve 64 bayt secret desteklenir. Bkz. Kimlik doğrulama.

Ödeme intent’i

İlk istekten önce ödeme denemesini ve UUID idempotency anahtarını kalıcı kaydedin. Toplamı tarayıcıdan almak yerine sunucuda hesaplayın.

result = lunixi.payments.create_intent(
  {
    amount: 10_050,
    currency: 'TRY',
    orderId: order.public_id,
    callbackUrl: 'https://merchant.example/payments/return',
    basketItems: [{ id: 'item-1', name: 'Product', itemType: 'PHYSICAL', price: 10_050 }]
  },
  idempotency_key: order.persisted_payment_attempt_key
)
 
intent_id = result.fetch('data').fetch('paymentId')
checkout_token = result.fetch('data').fetch('token')

Varsayılan form mağaza içinde açılır; isteğe bağlı yönlendirme yalnız doğrulanmış backend paymentPageUrl adresini kullanır. Token ve adres şifrelenir; iki mod aynı Lunixi form ayarlarını paylaşır. PAN/CVV toplamayın.

⚠️

Intent/token, HTTP 2xx, dönüş sayfası veya ara split durumu tahsilat kanıtı değildir. İmzalı webhook’u kalıcı siparişle eşleştirin; sunucudan ödeme sorgusunda nihai durum, tutar ve para birimi uyuşmadan siparişi tamamlamayın.

Tutarlar pozitif ve JavaScript güvenli tam sayı aralığında olmalıdır. İstek kökünde yalnız amount kullanılır; eski kök tutar alanları null olsa bile reddedilir. Sepet ve split bacak toplamları ana toplamla aynı olmalıdır. Intent’te installment gönderilmez; taksit formda seçilir. preauth: true provizyon ister; capture ayrı işlemdir.

Yardımcı API’ler

İşlemAPI
Intentlunixi.payments.create_intent(body, idempotency_key:)
Ödeme okuma/listelunixi.payments.get(id), lunixi.payments.list(**filters)
Açık yöntemlerlunixi.payments.methods(payment_method: nil)
Capturelunixi.payments.capture(id, idempotency_key:, amount: nil)
Tam/kısmi iadelunixi.payments.refund(id, idempotency_key:, amount: nil, reason: nil)
Provizyon iptalilunixi.payments.void(id, idempotency_key:)
Splitlunixi.payments.split(body, idempotency_key:)
Split durum/iadelunixi.payments.get_split(id), lunixi.payments.refund_split(id, amount:, idempotency_key:, reason: nil)
Fraudlunixi.fraud.evaluate(body), lunixi.fraud.record_event(body, idempotency_key:)
İmzalı genel HTTPlunixi.request(method, path, body: nil, query: {}, idempotency_key: nil, signed: true, binary: false)

Capture/iade amount: nil, kalan tutarın gateway tarafından kullanılmasını ister. False, sıfır, string ve kesirli tutar reddedilir. Her capture/iade/iptal için ayrı, kalıcı anahtar saklayın. Belgelenmiş devam eden split durumlarındaki HTTP 400 yanıtları protokol sonucu olarak döndürülür; ödenmiş sayılmaz. Yanıt zarfı korunur.

Diğer dokümante gateway uçları aynı imzalı transport üzerinden çağrılabilir:

links = lunixi.request('GET', '/api/v1/payments/links', query: { limit: 20 })

Payment, fraud ve webhook için açık yardımcılar vardır. Diğer servisler yayımlanmış REST şemasıyla kullanılır; bu sürüm her ürün için ayrı tipli istemci sunduğunu iddia etmez.

Webhook

JSON parse işleminden önce özgün baytları alın. Lunixi imza header’larını string değerlerle aktarın:

verifier = Lunixi::WebhookVerifier.new(
  secret: ENV.fetch('LUNIXI_WEBHOOK_SECRET'),
  previous_secrets: []
)
 
verifier.process(raw_request_bytes, signature_headers, store: durable_transactional_store) do |event|
  # Intent/sipariş ve sunucu ödeme sorgusuyla mutabakat yapın.
  # Sipariş değişikliği ile olay kaydını aynı transaction içinde commit edin.
end

v2 HMAC-SHA256, eventId.timestamp.rawBody üzerinde constant-time karşılaştırılır. İmzalı kimlik/tip tutarlılığı kontrol edilir; yinelenmiş JSON anahtarları reddedilir. Varsayılan geçmiş/gelecek zaman toleransı 300 saniyedir. Rotasyonda en fazla iki önceki secret tutulabilir. Header isimleri büyük/küçük harfe duyarsızdır; yinelenmiş header reddedilir.

Yalnız doğrulama olay kaydı oluşturmaz. Store process_once(id:, digest:) { … } uygular: benzersiz receipt ve sipariş değişikliği tek veritabanı transaction’ında commit edilir; :processed / :duplicate döner. Aynı kimliğin farklı baytları Lunixi::ReplayConflict üretir. İşleyici hatasında receipt geri alınmalıdır; yeniden teslim kurtarabilir. Kayıtları süreçler ve yeniden başlatmalar arasında koruyun. Rails örneğinde benzersiz (connection_id, event_id) indeksiyle gerçek veritabanı implementasyonu vardır.

Hatalar ve tekrar deneme

Taban sınıf Lunixi::Error’dır. ConfigurationError, SignatureError, TransportError, RedirectError, WebhookError / ReplayConflict ve ApiError alt sınıfları ayrılır. API hatalarında status, güvenli code / request_id ve isteğe bağlı retry_after vardır; karşı tarafın gövdesi/hata metni eklenmez.

GET/HEAD ve idempotency anahtarlı mutation, ağ hatası veya HTTP 429/502/503/504 için varsayılan iki, en çok üç kez tekrarlanabilir. Gövde/anahtar değişmez; nonce/imza yenilenir. Güvenli 401 çağrısında token bir kez yenilenir. Anahtarsız mutation tekrarlanmaz. Belirsiz zaman aşımında özgün işlemi sorgulayın; yeni anahtarla tahsilat başlatmayın.

TLS sertifika/hostname doğrulaması zorunludur; yönlendirmeler ve path/credential enjeksiyonu engellenir. İstek 1 MiB, yanıt 8 MiB ile sınırlıdır. transport:, clock:, sleeper: testlerde enjekte edilebilir. Transport Lunixi::Response.new(status:, headers:, body:) döndürmeli ve TLS/yönlendirme/thread-safety koşullarını korumalıdır. ca_file: doğrulamayı kapatmadan özel CA sağlar; ortam proxy kimlik bilgileri otomatik aktarılmaz.

Testler ve Rails

bundle exec rake test rails:test
bundle exec rubocop
bundle exec bundle-audit check --update
bundle exec brakeman --force --rails8 --path samples/rails --no-pager

Testler kriptografik vektörleri, bozulmuş imzayı, replay/rotasyonu, imzalanan baytları, tutarları, eşzamanlı token’ı, retry’ı, gerçek yerel TLS ve Rails transaction’larını kapsar. Node.js bağımsız JSON.stringify karşılaştırması sağlar. Sentetik testler para taşımaz; canlı kabul testi için kayıtlı TEST üye işyeri gerekir. Rails rehberi ve güvenlik politikası (opens in a new tab).

Mağaza içi form ve isteğe bağlı yönlendirme

Rails örneğinde varsayılan LUNIXI_CHECKOUT_MODE=embedded değeridir. Mevcut Lunixi formu sabit resmi CDN, HTML için kaçırılmış JSON data attribute ve yanıta özgü CSP nonce ile mağaza sayfasında açılır. Lunixi::Checkout.browser_config(token) yalnız resmi object/mount yapılandırmasını verir; gateway HTML'i veya merchant secret'ı basılmaz.

İsteğe bağlı yönlendirme için açıkça LUNIXI_CHECKOUT_MODE=hosted seçin. Lunixi::Checkout.payment_page_url(data.fetch('paymentPageUrl')) backend adresini birebir doğrular: HTTPS pay.lunixi.com/payment/ ve 16–4096 karakter ASCII opak değer; userinfo, port, sorgu, fragment veya kontrol karakteri kabul edilmez. Bu değer gömülü token'dan farklı olabilir; token'dan URL üretilmez. İki mod aynı açık yöntemleri, çoklu kartı, taksiti ve sigortayı sunar; yönlendirme şart değildir. Sabit toplamlı tek-intent tahsilat sınırlaması korunur.

0.1.2'yi açmadan önce rails db:migrate çalıştırın. Yeni istek/mod/dönüş adresi/zaman ağdan önce kaydedilir; gövde, token ve backend adresi Active Record Encryption ile şifrelenir. Tekrar aynı gövde/anahtarla gider; 23 saatten eski finansal gönderim durur. Mevcut satırlar eski hosted akışı olarak işaretlenir; bilinmeyen eski istek veya eksik backend adresi origin/anahtar değiştirilmeden ve adres üretilmeden mutabakat bekler. Genel mod değişikliği yeni rezervasyonlara uygulanır; mevcut istek kayıtlı modunu korur. CSRF/demo yetkisi, ham webhook, atomik receipt ve sınırlı 3DS/çoklu kart dönüşü korunur.

0.1.2 odaklı kontrolü: 21 Rails form/dönüş/URL güvenlik testi ve 117 assertion, 2 SDK yardımcı testi ve 5 assertion, RuboCop ve Brakeman (belgeli tek strict-URL yönlendirme false positive'i). Önceki tam 0.1.1 sonuçları temel kanıttır; canlı/merchant kabulü ertelenmiştir.