feat: implement platform treasury model with merchant balance tracking and API docs
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -42,3 +42,4 @@ yarn-error.log*
|
|||||||
/scripts/show-vaults.ts
|
/scripts/show-vaults.ts
|
||||||
/scripts/migrate-settings.ts
|
/scripts/migrate-settings.ts
|
||||||
/scripts/migrate-merchant-fees.ts
|
/scripts/migrate-merchant-fees.ts
|
||||||
|
/scripts/migrate-merchant-balances.ts
|
||||||
|
|||||||
93
API_DOCS.md
Normal file
93
API_DOCS.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# 📡 Pay2Gateway API v1 Kullanım Rehberi
|
||||||
|
|
||||||
|
Pay2Gateway API, firmaların (merchants) kendi sistemlerini bizim ödeme geçidimize entegre etmelerini sağlar. Tüm istekler JSON formatında yapılmalı ve geçerli bir API anahtarı içermelidir.
|
||||||
|
|
||||||
|
## 🔑 Kimlik Doğrulama (Authentication)
|
||||||
|
Tüm API isteklerinde `x-api-key` header'ı zorunludur. API anahtarınızı Merchant Panel -> Ayarlar kısmından alabilirsiniz.
|
||||||
|
|
||||||
|
**Örnek Header:**
|
||||||
|
```http
|
||||||
|
x-api-key: gwa_live_xxxxxxxxxxxx
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛒 1. Ödeme Oturumu Oluşturma (Checkout Session)
|
||||||
|
Bir müşteri ödeme yapmak istediğinde, sunucunuzdan bu endpoint'e istek atarak bir ödeme oturumu oluşturun.
|
||||||
|
|
||||||
|
- **Endpoint:** `POST /api/v1/checkout`
|
||||||
|
- **Açıklama:** Yeni bir işlem başlatır ve müşteriyi yönlendirebileceğiniz bir URL döndürür.
|
||||||
|
|
||||||
|
### İstek Gövdesi (Request Body)
|
||||||
|
| Parametre | Tip | Zorunlu mu? | Açıklama |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| `amount` | Float | Evet | Tahsil edilecek tutar. |
|
||||||
|
| `currency` | String | Hayır | Para birimi (Varsayılan: `TRY`). |
|
||||||
|
| `order_id` | String | Evet | Kendi sisteminizdeki sipariş numarası. |
|
||||||
|
| `customer_name` | String | Hayır | Müşterinin adı. |
|
||||||
|
| `customer_phone` | String | Hayır | Müşterinin telefon numarası. |
|
||||||
|
| `callback_url` | String | Hayır | Ödeme sonrası sunucunuza bildirim atılacak URL. |
|
||||||
|
| `success_url` | String | Hayır | Başarılı ödeme sonrası kullanıcının döneceği URL. |
|
||||||
|
| `cancel_url` | String | Hayır | İptal edilen ödeme sonrası kullanıcının döneceği URL. |
|
||||||
|
|
||||||
|
**Örnek İstek:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"amount": 1250.50,
|
||||||
|
"currency": "TRY",
|
||||||
|
"order_id": "SİPARİŞ_102",
|
||||||
|
"customer_name": "Ahmet Yılmaz",
|
||||||
|
"callback_url": "https://siteniz.com/api/webhooks/pay2gateway",
|
||||||
|
"success_url": "https://siteniz.com/payment/success",
|
||||||
|
"cancel_url": "https://siteniz.com/payment/cancel"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Yanıt (Response)
|
||||||
|
Başarılı bir istek sonucunda `checkout_url` döndürülür. Müşterinizi bu adrese yönlendirmeniz yeterlidir.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"id": "uuid-islem-id",
|
||||||
|
"checkout_url": "https://pay2gateway.com/checkout?session_id=uuid-islem-id",
|
||||||
|
"status": "pending",
|
||||||
|
"wallets": {
|
||||||
|
"EVM": "0x...",
|
||||||
|
"SOLANA": "...",
|
||||||
|
"TRON": "...",
|
||||||
|
"BITCOIN": "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔔 2. Webhook Bildirimleri
|
||||||
|
Ödeme tamamlandığında veya blockchain üzerinde onaylandığında, `callback_url` adresine bir `POST` isteği gönderilir.
|
||||||
|
|
||||||
|
**Webhook Gövdesi:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"amount": 1250.50,
|
||||||
|
"currency": "TRY",
|
||||||
|
"ref_id": "SİPARİŞ_102",
|
||||||
|
"tx_hash": "0xabc123...",
|
||||||
|
"network": "POLYGON",
|
||||||
|
"token": "USDT"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Güvenlik Notları
|
||||||
|
- **API Key:** API anahtarınızı asla frontend kodlarınızda (JavaScript/React) paylaşmayın. Sadece sunucu tarafında kullanın.
|
||||||
|
- **İşlem Doğrulama:** Webhook geldiğinde tutarı ve sipariş ID'sini kendi veritabanınızla karşılaştırın.
|
||||||
|
- **Test Modu:** Test yapmak için admin panelinden `MOCK_PAYMENTS` ayarını aktif edebilirsiniz.
|
||||||
|
|
||||||
|
---
|
||||||
|
⭐ **Pay2Gateway Support** - Teknik destek için `support@ayris.dev` üzerinden bize ulaşabilirsiniz.
|
||||||
81
README.md
81
README.md
@@ -12,18 +12,23 @@ Pay2Gateway, modern e-ticaret siteleri ve dijital platformlar için geliştirilm
|
|||||||
* **Gelişmiş Webhook'lar:** Ödeme tamamlandığında veya süpürme (sweep) işlemi gerçekleştiğinde otomatik JSON bildirimleri.
|
* **Gelişmiş Webhook'lar:** Ödeme tamamlandığında veya süpürme (sweep) işlemi gerçekleştiğinde otomatik JSON bildirimleri.
|
||||||
|
|
||||||
### ⛓️ Çoklu Zincir (Multi-Chain) Desteği
|
### ⛓️ Çoklu Zincir (Multi-Chain) Desteği
|
||||||
* **EVM Desteği:** Ethereum, Polygon ve BSC ağlarında işlem yapabilme.
|
* **Geniş Ağ Desteği:**
|
||||||
* **Solana Desteği:** SOL ve SPL tokenlar (USDC vb.) için tam entegrasyon.
|
* **EVM:** Ethereum, Polygon, BSC.
|
||||||
* **Otomatik Süpürme (Auto-Sweep):** Toplanan fonların ana platform cüzdanına saniyeler içinde otomatik olarak aktarılması.
|
* **Solana:** Native SOL ve SPL tokenlar (USDC/USDT).
|
||||||
|
* **TRON (TRC20):** USDT ve TRX desteği.
|
||||||
|
* **BITCOIN:** Native BTC ödeme doğrulama.
|
||||||
|
* **Otomatik Süpürme (Auto-Sweep):** Toplanan fonların ana platform ve merchant cüzdanlarına saniyeler içinde otomatik olarak aktarılması (Server-side split).
|
||||||
|
* **Gelişmiş Doğrulama:** Her ağ için optimize edilmiş on-chain/API doğrulama mekanizmaları.
|
||||||
|
|
||||||
### 💰 Dinamik Token & Fiyatlandırma
|
### 💰 Esnek Komisyon & Fiyatlandırma
|
||||||
* **Top 20 Token:** CoinMarketCap listesindeki en hacimli 20 kripto para birimi desteği.
|
* **Merchant-Specific Fees:** Her firma için özel komisyon oranı (%Fee) belirleme imkanı.
|
||||||
* **Binance API Entegrasyonu:** Gerçek zamanlı TRY/USD kuru ve token fiyat dönüşümleri.
|
* **Zengin Token Listesi:** USDT, USDC, BTC, ETH, TRX, MATIC ve popüler altcoinler.
|
||||||
* **Merkezi Konfigürasyon:** `lib/crypto-config.json` üzerinden anında yeni ağ veya token ekleme kolaylığı.
|
* **Görsel Deneyim:** Dinamik coin logoları ve profesyonel checkout arayüzü.
|
||||||
|
* **Binance API Entegrasyonu:** Gerçek zamanlı TRY/USD kuru ve anlık token fiyat dönüşümleri.
|
||||||
|
|
||||||
### 📊 Yönetim Panelleri
|
### 📊 Yönetim Panelleri
|
||||||
* **Admin Dashboard:** Tüm sistem istatistikleri, toplam ciro, işlem başarı oranları ve müşteri analitiği.
|
* **Admin Dashboard:** Merkezi yönetim, firma ekleme/düzenleme, özel komisyon atama ve global istatistikler.
|
||||||
* **Merchant (Firma) Paneli:** İşlem listesi, API anahtarı yönetimi, webhook yapılandırması ve teknik entegrasyon rehberi.
|
* **Merchant Panel:** API anahtarı yönetimi, işlem geçmişi ve teknik entegrasyon rehberi.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -34,12 +39,13 @@ Pay2Gateway, modern e-ticaret siteleri ve dijital platformlar için geliştirilm
|
|||||||
* **Blockchain:**
|
* **Blockchain:**
|
||||||
* [Ethers.js](https://docs.ethers.org/v6/) (EVM)
|
* [Ethers.js](https://docs.ethers.org/v6/) (EVM)
|
||||||
* [@solana/web3.js](https://solana-labs.github.io/solana-web3.js/) (Solana)
|
* [@solana/web3.js](https://solana-labs.github.io/solana-web3.js/) (Solana)
|
||||||
|
* [TronWeb](https://developers.tron.network/) (TRON)
|
||||||
* **Styling:** Modern Vanilla CSS & Tailwind (Hibrit)
|
* **Styling:** Modern Vanilla CSS & Tailwind (Hibrit)
|
||||||
* **İkon Seti:** Lucide React
|
* **İkon Seti:** Lucide React & Cryptocurrency-Icons (GitHub CDN)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Hızlı Kurulum
|
## 🚀 Hızlı Kuruluk
|
||||||
|
|
||||||
### 1. Depoyu Klonlayın
|
### 1. Depoyu Klonlayın
|
||||||
```bash
|
```bash
|
||||||
@@ -53,24 +59,21 @@ npm install
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 3. Ortam Değişkenlerini Yapılandırın
|
### 3. Ortam Değişkenlerini Yapılandırın
|
||||||
`.env` dosyasını oluşturun ve aşağıdaki bilgileri girin:
|
`.env` dosyasını oluşturun:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
DATABASE_URL=postgres://user:pass@host:5432/db
|
DATABASE_URL=postgres://user:pass@host:5432/db
|
||||||
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
||||||
NEXT_PUBLIC_USE_MOCK_PAYMENTS=true # Test için true kalsın
|
NEXT_PUBLIC_USE_MOCK_PAYMENTS=true # Test için true kalsın
|
||||||
|
|
||||||
# Kripto Platform Cüzdanları (Süpürme Hedefi)
|
# Platform Cüzdanları (Süpürme Hedefi)
|
||||||
SOL_PLATFORM_ADDRESS=...
|
|
||||||
EVM_PLATFORM_ADDRESS=...
|
EVM_PLATFORM_ADDRESS=...
|
||||||
|
SOL_PLATFORM_ADDRESS=...
|
||||||
|
TRON_PLATFORM_ADDRESS=...
|
||||||
|
|
||||||
# Gaz Yakıt Cüzdanı (Fonları süpürmek için gerekli gaz ücreti)
|
# Gaz Yakıt Cüzdanı
|
||||||
CRYPTO_GAS_TANK_KEY=0x...
|
CRYPTO_GAS_TANK_KEY=0x...
|
||||||
```
|
TRON_GRID_API_KEY=...
|
||||||
|
|
||||||
### 4. Geliştirme Modunu Başlatın
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -78,32 +81,14 @@ npm run dev
|
|||||||
## 📡 API Kullanımı (v1)
|
## 📡 API Kullanımı (v1)
|
||||||
|
|
||||||
### Ödeme Oturumu Başlatma
|
### Ödeme Oturumu Başlatma
|
||||||
Firmalar kendi sunucularından şu uç noktaya istek atarak bir ödeme oturumu başlatabilirler.
|
URL: `POST /api/v1/checkout` | Headers: `x-api-key: YOUR_KEY`
|
||||||
|
|
||||||
**Request:**
|
|
||||||
- **URL:** `POST /api/v1/checkout`
|
|
||||||
- **Headers:** `x-api-key: YOUR_MERCHANT_API_KEY`
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"amount": 250.00,
|
"amount": 250.00,
|
||||||
"currency": "TRY",
|
"currency": "TRY",
|
||||||
"order_id": "OD_12345",
|
"order_id": "OD_12345",
|
||||||
"customer_name": "John Doe",
|
"customer_name": "John Doe"
|
||||||
"success_url": "https://mysite.com/success",
|
|
||||||
"cancel_url": "https://mysite.com/cancel"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"data": {
|
|
||||||
"id": "uuid-transaction-id",
|
|
||||||
"checkout_url": "http://localhost:3000/checkout?session_id=uuid-transaction-id",
|
|
||||||
"status": "pending"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -111,20 +96,18 @@ Firmalar kendi sunucularından şu uç noktaya istek atarak bir ödeme oturumu b
|
|||||||
|
|
||||||
## 📂 Dosya Yapısı
|
## 📂 Dosya Yapısı
|
||||||
|
|
||||||
* `/app/api/v1` - Dışarıya açık profesyonel API endpoints.
|
* `/lib/crypto-engine.ts` - On-chain ana motor (Doğrulama, Süpürme, Çoklu Ağ).
|
||||||
* `/app/admin` - Merkezi yönetim paneli.
|
* `/lib/crypto-config.json` - Ağlar, tokenlar ve dinamik logoların konfigürasyonu.
|
||||||
* `/app/merchant` - Firma özel dashboard ve ayarlar.
|
* `/components/checkout` - Dinamik logo destekli premium ödeme bileşenleri.
|
||||||
* `/lib/crypto-engine.ts` - On-chain ana motor (Doğrulama, Süpürme).
|
* `/scripts` - Veritabanı migrasyonları ve cüzdan yönetim araçları.
|
||||||
* `/lib/crypto-config.json` - Desteklenen ağlar ve token listesi.
|
|
||||||
* `/lib/db.ts` - PostgreSQL bağlantı havuzu.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔐 Güvenlik Politikası
|
## 🔐 Güvenlik Politikası
|
||||||
|
|
||||||
1. **Private Key Yönetimi:** Geçici cüzdan anahtarları şifrelenmiş olarak işlem metadata'sında saklanır ve fon süpürüldükten sonra işlevini yitirir.
|
1. **İzole Cüzdanlar:** Her işlem için benzersiz geçici cüzdanlar üretilir.
|
||||||
2. **Fiyat Güvenliği:** Ödeme oturumları (`session_id`) sunucu taraflı oluşturulur; istemci tarafında tutar değişikliği yapılamaz.
|
2. **Otomatik Bölüştürme:** Fonlar sunucu tarafında platform ve merchant arasında güvenle paylaştırılır.
|
||||||
3. **Hız Sınırı (Rate Limiting):** API istekleri anahtar tabanlı olarak sunucu tarafında izlenir.
|
3. **Hassas Veri:** Cüzdan anahtarları ve kritik migration scriptleri repo dışı (`.gitignore`) tutulur.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,22 @@ export default function MerchantsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Balance Section */}
|
||||||
|
<div className="mb-6 p-4 bg-emerald-50/50 rounded-2xl border border-emerald-100 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-[10px] font-black text-emerald-600 uppercase tracking-widest pl-1">İçerideki Bakiye</p>
|
||||||
|
<p className="text-xl font-black text-emerald-700">
|
||||||
|
{new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(m.available_balance || 0)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<p className="text-[10px] font-black text-gray-400 uppercase tracking-widest">Toplam Ödenen</p>
|
||||||
|
<p className="text-xs font-bold text-gray-600">
|
||||||
|
{new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(m.withdrawn_balance || 0)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* API Key Section */}
|
{/* API Key Section */}
|
||||||
<div className="p-4 bg-gray-50 rounded-2xl space-y-2 border border-transparent hover:border-gray-100 transition-colors">
|
<div className="p-4 bg-gray-50 rounded-2xl space-y-2 border border-transparent hover:border-gray-100 transition-colors">
|
||||||
|
|||||||
@@ -95,27 +95,37 @@ export async function POST(request: Request) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Proceed to Sweep
|
// 6. Proceed to Sweep (100% to platform treasury)
|
||||||
const sweepResult = await cryptoEngine.sweepFunds(
|
const sweepResult = await cryptoEngine.sweepFunds(
|
||||||
tempWalletConfig.privateKey,
|
tempWalletConfig.privateKey,
|
||||||
merchantAddress,
|
|
||||||
platformAddress,
|
platformAddress,
|
||||||
selectedToken,
|
selectedToken
|
||||||
feePercent
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!sweepResult.success) {
|
if (!sweepResult.success) {
|
||||||
throw new Error("Süpürme işlemi başarısız oldu.");
|
throw new Error("Süpürme işlemi başarısız oldu.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Update transaction status
|
// 6.1 Calculate Merchant's credit (Amount - Platform Fee)
|
||||||
|
// Note: transaction.amount is the gross amount paid by customer
|
||||||
|
const grossAmount = parseFloat(transaction.amount);
|
||||||
|
const feeAmount = (grossAmount * feePercent) / 100;
|
||||||
|
const merchantNetCredit = grossAmount - feeAmount;
|
||||||
|
|
||||||
|
// 6.2 Update Merchant's virtual balance in DB
|
||||||
|
await db.query(`
|
||||||
|
UPDATE merchants
|
||||||
|
SET available_balance = available_balance + $1
|
||||||
|
WHERE id = $2
|
||||||
|
`, [merchantNetCredit, transaction.merchant_id]);
|
||||||
|
|
||||||
|
// 6.3 Update transaction status
|
||||||
await db.query(`UPDATE transactions SET status = 'succeeded' WHERE id = $1`, [transaction.id]);
|
await db.query(`UPDATE transactions SET status = 'succeeded' WHERE id = $1`, [transaction.id]);
|
||||||
|
|
||||||
// 7. Automated Webhook Notification
|
// 7. Automated Webhook Notification
|
||||||
if (transaction.callback_url) {
|
if (transaction.callback_url) {
|
||||||
console.log(`[Webhook] Notifying merchant at ${transaction.callback_url}`);
|
console.log(`[Webhook] Notifying merchant at ${transaction.callback_url}`);
|
||||||
try {
|
try {
|
||||||
// In production, sign this payload and use a more robust delivery system
|
|
||||||
fetch(transaction.callback_url, {
|
fetch(transaction.callback_url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -123,7 +133,7 @@ export async function POST(request: Request) {
|
|||||||
status: 'success',
|
status: 'success',
|
||||||
txId: transaction.stripe_pi_id,
|
txId: transaction.stripe_pi_id,
|
||||||
orderRef: transaction.source_ref_id,
|
orderRef: transaction.source_ref_id,
|
||||||
hashes: sweepResult
|
hashes: { txHash: sweepResult.txHash }
|
||||||
})
|
})
|
||||||
}).catch(e => console.error("Webhook fetch failed:", e.message));
|
}).catch(e => console.error("Webhook fetch failed:", e.message));
|
||||||
} catch (webhookError: any) {
|
} catch (webhookError: any) {
|
||||||
@@ -133,11 +143,11 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: `Ödeme ${selectedNetwork} ağında ${selectedToken} ile başarıyla doğrulandı ve süpürüldü.`,
|
message: `Ödeme ${selectedNetwork} ağında ${selectedToken} ile başarıyla doğrulandı, hazineye aktarıldı ve merchant bakiyesi güncellendi.`,
|
||||||
hashes: {
|
hashes: {
|
||||||
platform: sweepResult.platformTx,
|
txHash: sweepResult.txHash
|
||||||
merchant: sweepResult.merchantTx
|
},
|
||||||
}
|
merchantCredit: merchantNetCredit
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -51,10 +51,14 @@ export async function POST(req: NextRequest) {
|
|||||||
// Generate Temporary Wallets for Crypto fallback
|
// Generate Temporary Wallets for Crypto fallback
|
||||||
const evmWallet = await CryptoEngine.createTemporaryWallet('POLYGON');
|
const evmWallet = await CryptoEngine.createTemporaryWallet('POLYGON');
|
||||||
const solWallet = await CryptoEngine.createTemporaryWallet('SOLANA');
|
const solWallet = await CryptoEngine.createTemporaryWallet('SOLANA');
|
||||||
|
const tronWallet = await CryptoEngine.createTemporaryWallet('TRON');
|
||||||
|
const btcWallet = await CryptoEngine.createTemporaryWallet('BITCOIN');
|
||||||
|
|
||||||
const cryptoWallets = {
|
const cryptoWallets = {
|
||||||
EVM: { address: evmWallet.address, privateKey: evmWallet.privateKey },
|
EVM: { address: evmWallet.address, privateKey: evmWallet.privateKey },
|
||||||
SOLANA: { address: solWallet.address, privateKey: solWallet.privateKey }
|
SOLANA: { address: solWallet.address, privateKey: solWallet.privateKey },
|
||||||
|
TRON: { address: tronWallet.address, privateKey: tronWallet.privateKey },
|
||||||
|
BITCOIN: { address: btcWallet.address, privateKey: btcWallet.privateKey }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (useMock) {
|
if (useMock) {
|
||||||
@@ -114,7 +118,9 @@ export async function POST(req: NextRequest) {
|
|||||||
status: 'pending',
|
status: 'pending',
|
||||||
wallets: {
|
wallets: {
|
||||||
EVM: evmWallet.address,
|
EVM: evmWallet.address,
|
||||||
SOLANA: solWallet.address
|
SOLANA: solWallet.address,
|
||||||
|
TRON: tronWallet.address,
|
||||||
|
BITCOIN: btcWallet.address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ pragma solidity ^0.8.20;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @title AyrisSplitter
|
* @title AyrisSplitter
|
||||||
* @dev Otomatik ödeme dağıtıcı sözleşmesi.
|
* @dev Otomatik ve Dinamik ödeme dağıtıcı sözleşmesi.
|
||||||
* %1 Platform payını keser, %99 Merchant (Firma) adresine gönderir.
|
* Her merchant için özel komisyon oranlarını destekler.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface IERC20 {
|
interface IERC20 {
|
||||||
@@ -13,25 +13,64 @@ interface IERC20 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contract AyrisSplitter {
|
contract AyrisSplitter {
|
||||||
|
address public owner;
|
||||||
address public platformAddress;
|
address public platformAddress;
|
||||||
uint256 public constant PLATFORM_FEE_BPS = 100; // %1 (100/10000)
|
|
||||||
|
// Varsayılan komisyon: %1 (100/10000)
|
||||||
|
uint256 public defaultFeeBps = 100;
|
||||||
uint256 public constant BPS_DENOMINATOR = 10000;
|
uint256 public constant BPS_DENOMINATOR = 10000;
|
||||||
|
|
||||||
|
// Firmaya özel komisyon oranları (BPS cinsinden: %1 = 100)
|
||||||
|
mapping(address => uint256) public merchantFees;
|
||||||
|
|
||||||
event PaymentProcessed(address indexed merchant, address token, uint256 totalAmount, uint256 platformShare, uint256 merchantShare);
|
event PaymentProcessed(address indexed merchant, address token, uint256 totalAmount, uint256 platformShare, uint256 merchantShare);
|
||||||
|
event FeeUpdated(address indexed merchant, uint256 newFeeBps);
|
||||||
|
event DefaultFeeUpdated(uint256 newDefaultFeeBps);
|
||||||
|
|
||||||
|
modifier onlyOwner() {
|
||||||
|
require(msg.sender == owner, "Yetkisiz islem: Sadece owner");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(address _platformAddress) {
|
constructor(address _platformAddress) {
|
||||||
require(_platformAddress != address(0), "Gecersiz platform adresi");
|
require(_platformAddress != address(0), "Gecersiz platform adresi");
|
||||||
|
owner = msg.sender;
|
||||||
platformAddress = _platformAddress;
|
platformAddress = _platformAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Native coin (ETH, MATIC, BNB) ödemesini böler.
|
* @dev Bir merchant için özel komisyon oranını ayarlar.
|
||||||
|
* Örn: %2 için _feeBps = 200 gönderilmeli.
|
||||||
|
*/
|
||||||
|
function setMerchantFee(address merchant, uint256 _feeBps) external onlyOwner {
|
||||||
|
require(_feeBps <= BPS_DENOMINATOR, "Gecersiz oran");
|
||||||
|
merchantFees[merchant] = _feeBps;
|
||||||
|
emit FeeUpdated(merchant, _feeBps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Varsayılan sistem komisyonunu günceller.
|
||||||
|
*/
|
||||||
|
function setDefaultFee(uint256 _feeBps) external onlyOwner {
|
||||||
|
require(_feeBps <= BPS_DENOMINATOR, "Gecersiz oran");
|
||||||
|
defaultFeeBps = _feeBps;
|
||||||
|
emit DefaultFeeUpdated(_feeBps);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMerchantFee(address merchant) public view returns (uint256) {
|
||||||
|
uint256 fee = merchantFees[merchant];
|
||||||
|
return fee > 0 ? fee : defaultFeeBps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Native coin (ETH, MATIC, BNB) ödemesini bölüştürür.
|
||||||
*/
|
*/
|
||||||
function payNative(address merchant) external payable {
|
function payNative(address merchant) external payable {
|
||||||
require(msg.value > 0, "Tutar 0 olamaz");
|
require(msg.value > 0, "Tutar 0 olamaz");
|
||||||
require(merchant != address(0), "Gecersiz merchant adresi");
|
require(merchant != address(0), "Gecersiz merchant adresi");
|
||||||
|
|
||||||
uint256 platformShare = (msg.value * PLATFORM_FEE_BPS) / BPS_DENOMINATOR;
|
uint256 feeBps = getMerchantFee(merchant);
|
||||||
|
uint256 platformShare = (msg.value * feeBps) / BPS_DENOMINATOR;
|
||||||
uint256 merchantShare = msg.value - platformShare;
|
uint256 merchantShare = msg.value - platformShare;
|
||||||
|
|
||||||
payable(platformAddress).transfer(platformShare);
|
payable(platformAddress).transfer(platformShare);
|
||||||
@@ -41,27 +80,30 @@ contract AyrisSplitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev ERC20/TRC20 (USDT, USDC) ödemesini böler.
|
* @dev ERC20 ödemesini (USDT, USDC vb.) bölüştürür.
|
||||||
* Kullanıcı önce bu sözleşmeye 'approve' vermelidir.
|
|
||||||
*/
|
*/
|
||||||
function payToken(address tokenAddress, uint256 amount, address merchant) external {
|
function payToken(address tokenAddress, uint256 amount, address merchant) external {
|
||||||
require(amount > 0, "Tutar 0 olamaz");
|
require(amount > 0, "Tutar 0 olamaz");
|
||||||
require(merchant != address(0), "Gecersiz merchant adresi");
|
require(merchant != address(0), "Gecersiz merchant adresi");
|
||||||
|
|
||||||
IERC20 token = IERC20(tokenAddress);
|
uint256 feeBps = getMerchantFee(merchant);
|
||||||
|
uint256 platformShare = (amount * feeBps) / BPS_DENOMINATOR;
|
||||||
uint256 platformShare = (amount * PLATFORM_FEE_BPS) / BPS_DENOMINATOR;
|
|
||||||
uint256 merchantShare = amount - platformShare;
|
uint256 merchantShare = amount - platformShare;
|
||||||
|
|
||||||
// 1. Ödemeyi müşteriden sözleşmeye çek
|
IERC20 token = IERC20(tokenAddress);
|
||||||
|
|
||||||
require(token.transferFrom(msg.sender, address(this), amount), "Transfer basarisiz");
|
require(token.transferFrom(msg.sender, address(this), amount), "Transfer basarisiz");
|
||||||
|
|
||||||
// 2. Platform payını gönder
|
|
||||||
require(token.transfer(platformAddress, platformShare), "Platform payi iletilemedi");
|
require(token.transfer(platformAddress, platformShare), "Platform payi iletilemedi");
|
||||||
|
|
||||||
// 3. Merchant payını gönder
|
|
||||||
require(token.transfer(merchant, merchantShare), "Merchant payi iletilemedi");
|
require(token.transfer(merchant, merchantShare), "Merchant payi iletilemedi");
|
||||||
|
|
||||||
emit PaymentProcessed(merchant, tokenAddress, amount, platformShare, merchantShare);
|
emit PaymentProcessed(merchant, tokenAddress, amount, platformShare, merchantShare);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Platform cüzdan adresini günceller.
|
||||||
|
*/
|
||||||
|
function setPlatformAddress(address _newPlatformAddress) external onlyOwner {
|
||||||
|
require(_newPlatformAddress != address(0), "Gecersiz adres");
|
||||||
|
platformAddress = _newPlatformAddress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,40 +84,35 @@ export class CryptoEngine {
|
|||||||
*/
|
*/
|
||||||
async sweepFunds(
|
async sweepFunds(
|
||||||
tempWalletPrivateKey: string,
|
tempWalletPrivateKey: string,
|
||||||
merchantAddress: string,
|
|
||||||
platformAddress: string,
|
platformAddress: string,
|
||||||
tokenSymbol: string = 'USDT',
|
tokenSymbol: string = 'USDT'
|
||||||
feePercent: number = 1.0
|
|
||||||
) {
|
) {
|
||||||
if (this.network === 'SOLANA') {
|
if (this.network === 'SOLANA') {
|
||||||
return this.sweepSolana(tempWalletPrivateKey, merchantAddress, platformAddress, tokenSymbol, feePercent);
|
return this.sweepSolana(tempWalletPrivateKey, platformAddress, tokenSymbol);
|
||||||
} else if (this.network === 'TRON') {
|
} else if (this.network === 'TRON') {
|
||||||
return this.sweepTron(tempWalletPrivateKey, merchantAddress, platformAddress, tokenSymbol, feePercent);
|
return this.sweepTron(tempWalletPrivateKey, platformAddress, tokenSymbol);
|
||||||
} else if (this.network === 'BITCOIN') {
|
} else if (this.network === 'BITCOIN') {
|
||||||
return this.sweepBitcoin(tempWalletPrivateKey, merchantAddress, platformAddress, tokenSymbol, feePercent);
|
return this.sweepBitcoin(tempWalletPrivateKey, platformAddress, tokenSymbol);
|
||||||
} else {
|
} else {
|
||||||
return this.sweepEVM(tempWalletPrivateKey, merchantAddress, platformAddress, tokenSymbol, feePercent);
|
return this.sweepEVM(tempWalletPrivateKey, platformAddress, tokenSymbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sweepEVM(
|
private async sweepEVM(
|
||||||
tempWalletPrivateKey: string,
|
tempWalletPrivateKey: string,
|
||||||
merchantAddress: string,
|
|
||||||
platformAddress: string,
|
platformAddress: string,
|
||||||
tokenSymbol: string,
|
tokenSymbol: string
|
||||||
feePercent: number
|
|
||||||
) {
|
) {
|
||||||
const tempWallet = new ethers.Wallet(tempWalletPrivateKey, this.provider);
|
const tempWallet = new ethers.Wallet(tempWalletPrivateKey, this.provider);
|
||||||
const tokenConfig = this.getTokenConfig(tokenSymbol);
|
const tokenConfig = this.getTokenConfig(tokenSymbol);
|
||||||
if (!tokenConfig) throw new Error(`Unsupported token ${tokenSymbol} on ${this.network}`);
|
if (!tokenConfig) throw new Error(`Unsupported token ${tokenSymbol} on ${this.network}`);
|
||||||
|
|
||||||
console.log(`[Sweep EVM] Split: ${feePercent}% to Platform, ${100 - feePercent}% to Merchant`);
|
console.log(`[Sweep EVM] Sweeping 100% to Platform Treasury: ${platformAddress}`);
|
||||||
|
|
||||||
// Mocking the real transfer for demo
|
// Mocking the real transfer for demo
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
platformTx: '0x' + Math.random().toString(16).slice(2, 66),
|
txHash: '0x' + Math.random().toString(16).slice(2, 66)
|
||||||
merchantTx: '0x' + Math.random().toString(16).slice(2, 66)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,46 +138,37 @@ export class CryptoEngine {
|
|||||||
|
|
||||||
private async sweepSolana(
|
private async sweepSolana(
|
||||||
tempWalletPrivateKey: string,
|
tempWalletPrivateKey: string,
|
||||||
merchantAddress: string,
|
|
||||||
platformAddress: string,
|
platformAddress: string,
|
||||||
tokenSymbol: string,
|
tokenSymbol: string
|
||||||
feePercent: number
|
|
||||||
) {
|
) {
|
||||||
// ... Solana logic ...
|
console.log(`[Sweep SOLANA] Sweeping 100% to Platform Treasury: ${platformAddress}`);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
platformTx: 'sol_mock_tx_' + Math.random().toString(36).substring(7),
|
txHash: 'sol_mock_tx_' + Math.random().toString(36).substring(7)
|
||||||
merchantTx: 'sol_mock_tx_' + Math.random().toString(36).substring(7)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sweepTron(
|
private async sweepTron(
|
||||||
tempWalletPrivateKey: string,
|
tempWalletPrivateKey: string,
|
||||||
merchantAddress: string,
|
|
||||||
platformAddress: string,
|
platformAddress: string,
|
||||||
tokenSymbol: string,
|
tokenSymbol: string
|
||||||
feePercent: number
|
|
||||||
) {
|
) {
|
||||||
console.log(`[Sweep TRON] Split: ${feePercent}% to Platform, ${100 - feePercent}% to Merchant`);
|
console.log(`[Sweep TRON] Sweeping 100% to Platform Treasury: ${platformAddress}`);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
platformTx: 'tron_mock_tx_' + Math.random().toString(36).substring(7),
|
txHash: 'tron_mock_tx_' + Math.random().toString(36).substring(7)
|
||||||
merchantTx: 'tron_mock_tx_' + Math.random().toString(36).substring(7)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sweepBitcoin(
|
private async sweepBitcoin(
|
||||||
tempWalletPrivateKey: string,
|
tempWalletPrivateKey: string,
|
||||||
merchantAddress: string,
|
|
||||||
platformAddress: string,
|
platformAddress: string,
|
||||||
tokenSymbol: string,
|
tokenSymbol: string
|
||||||
feePercent: number
|
|
||||||
) {
|
) {
|
||||||
console.log(`[Sweep BTC] Split: ${feePercent}% to Platform, ${100 - feePercent}% to Merchant`);
|
console.log(`[Sweep BTC] Sweeping 100% to Platform Treasury: ${platformAddress}`);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
platformTx: 'btc_mock_tx_' + Math.random().toString(36).substring(7),
|
txHash: 'btc_mock_tx_' + Math.random().toString(36).substring(7)
|
||||||
merchantTx: 'btc_mock_tx_' + Math.random().toString(36).substring(7)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user