From 89f6e5689f609d6b299ebf4eb8c5a294fea1a66a Mon Sep 17 00:00:00 2001 From: adevopg Date: Mon, 13 Jul 2026 17:14:29 +0000 Subject: [PATCH] =?UTF-8?q?d-points:=20dejar=20s=C3=B3lo=20Stripe=20y=20Su?= =?UTF-8?q?mUp=20con=20compra=20de=20PD=20funcional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Elimina las pestañas de PayPal/dLocal/Crypto/Skrill y por país; deja Información + Stripe + SumUp (las dos únicas pasarelas que se usarán). - Stripe: totalmente funcional reutilizando createCheckoutSession + webhook + página de éxito. Nueva ruta /api/dpoints/checkout crea la sesión con importe elegido por el usuario (1 unidad = 100 PD). - SumUp: integración real (lib/sumup.ts) mediante checkout hospedado, habilitada al definir SUMUP_API_KEY/SUMUP_MERCHANT_CODE; si no, avisa. - Nuevo servicio de entrega `dpoints` (lib/paid-services.ts) que acredita PD en home_api_points; entrega idempotente vía reclamo atómico. - Página /d-points-success entrega ambos proveedores (session_id/ref). - Iconos SVG generados para cada pasarela (stripe/sumup, marca + wordmark). - Moneda configurable con DP_CURRENCY (por defecto EUR). Co-Authored-By: Claude Opus 4.8 --- .../app/[locale]/d-points-success/page.tsx | 54 ++ web-next/app/[locale]/d-points/page.tsx | 19 +- web-next/app/api/dpoints/checkout/route.ts | 78 ++ web-next/app/globals.css | 8 + web-next/components/DPointsTabs.tsx | 826 ++++-------------- web-next/lib/dpoints.ts | 27 + web-next/lib/paid-services.ts | 9 + web-next/lib/sumup.ts | 108 +++ .../nw-images/nw-logos/nowpayments-logo.png | Bin 12640 -> 0 bytes .../nw-ryu/nw-images/nw-logos/stripe-logo.svg | 4 + .../nw-images/nw-logos/stripe-p-logo.svg | 4 + .../nw-ryu/nw-images/nw-logos/sumup-logo.svg | 5 + .../nw-images/nw-logos/sumup-p-logo.svg | 4 + 13 files changed, 453 insertions(+), 693 deletions(-) create mode 100644 web-next/app/[locale]/d-points-success/page.tsx create mode 100644 web-next/app/api/dpoints/checkout/route.ts create mode 100644 web-next/lib/dpoints.ts create mode 100644 web-next/lib/sumup.ts delete mode 100644 web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/nowpayments-logo.png create mode 100644 web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/stripe-logo.svg create mode 100644 web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/stripe-p-logo.svg create mode 100644 web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/sumup-logo.svg create mode 100644 web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/sumup-p-logo.svg diff --git a/web-next/app/[locale]/d-points-success/page.tsx b/web-next/app/[locale]/d-points-success/page.tsx new file mode 100644 index 0000000..ecddcdd --- /dev/null +++ b/web-next/app/[locale]/d-points-success/page.tsx @@ -0,0 +1,54 @@ +import { setRequestLocale } from 'next-intl/server' +import { Link } from '@/i18n/navigation' +import { fulfillCheckoutSession } from '@/lib/fulfill' +import { isSessionPaid } from '@/lib/stripe' +import { fulfillSumUpCheckout } from '@/lib/sumup' +import { PageShell } from '@/components/PageShell' + +export const dynamic = 'force-dynamic' + +export default async function DPointsSuccessPage({ + params, + searchParams, +}: { + params: Promise<{ locale: string }> + searchParams: Promise<{ provider?: string; session_id?: string; ref?: string }> +}) { + const { locale } = await params + setRequestLocale(locale) + const { provider, session_id, ref } = await searchParams + + // Entrega compartida con el webhook/return (idempotente vía reclamo atómico). + let status: 'ok' | 'already' | 'error' = 'error' + + if (provider === 'sumup' && ref) { + const r = await fulfillSumUpCheckout(ref) + if (r.ok) status = 'ok' + else if (r.paid) status = 'already' + } else if (session_id) { + const r = await fulfillCheckoutSession(session_id) + if (r.ok) status = 'ok' + else if (await isSessionPaid(session_id)) status = 'already' + } + + return ( + +
+
+ {status === 'ok' ? ( + ¡Pago completado! Tus PD ya fueron acreditados a tu cuenta. + ) : status === 'already' ? ( + Este pago ya había sido procesado. Tus PD están acreditados. + ) : ( + No pudimos confirmar tu pago. Si se realizó el cargo, contacta con soporte. + )} +
+
+

+ ← Regresar a mi cuenta +

+
+
+
+ ) +} diff --git a/web-next/app/[locale]/d-points/page.tsx b/web-next/app/[locale]/d-points/page.tsx index 173dba6..7d5323c 100644 --- a/web-next/app/[locale]/d-points/page.tsx +++ b/web-next/app/[locale]/d-points/page.tsx @@ -1,5 +1,4 @@ import type { Metadata } from 'next' -import { headers } from 'next/headers' import { setRequestLocale } from 'next-intl/server' import { redirect } from '@/i18n/navigation' import { getSession } from '@/lib/session' @@ -20,25 +19,11 @@ export default async function DPointsPage({ params }: { params: Promise<{ locale if (!session.username) redirect({ href: '/select-account', locale }) const realm = await getRealmName() - const h = await headers() - const ip = (h.get('x-forwarded-for') || '').split(',')[0].trim() || '0.0.0.0' - const host = h.get('x-forwarded-host') || h.get('host') || '' - const proto = h.get('x-forwarded-proto') || 'https' - const origin = host ? `${proto}://${host}` : '' - - // Pasarelas automáticas: se habilitan al configurar sus credenciales por entorno. - const paypalBusiness = process.env.PAYPAL_BUSINESS_EMAIL || '' + const currency = (process.env.DP_CURRENCY || 'eur').toLowerCase() return ( - + ) } diff --git a/web-next/app/api/dpoints/checkout/route.ts b/web-next/app/api/dpoints/checkout/route.ts new file mode 100644 index 0000000..408ce2d --- /dev/null +++ b/web-next/app/api/dpoints/checkout/route.ts @@ -0,0 +1,78 @@ +import { randomUUID } from 'crypto' +import { getSession } from '@/lib/session' +import { createCheckoutSession } from '@/lib/stripe' +import { createSumUpCheckout, sumupConfigured } from '@/lib/sumup' +import { PD_PER_UNIT, DP_MIN_AMOUNT, DP_MAX_AMOUNT } from '@/lib/dpoints' + +/** + * Inicia la compra de PD por Stripe o SumUp. El importe lo elige el usuario + * (1 unidad = 100 PD). Devuelve la URL de checkout a la que redirigir. + */ +export async function POST(request: Request) { + const session = await getSession() + if (!session.accountId || !session.username) { + return Response.json({ success: false, error: 'notAuthenticated' }, { status: 401 }) + } + + let body: Record = {} + try { + body = await request.json() + } catch { + return Response.json({ success: false, error: 'invalidRequest' }, { status: 400 }) + } + + const provider = String(body.provider ?? '') + if (provider !== 'stripe' && provider !== 'sumup') { + return Response.json({ success: false, error: 'invalidProvider' }, { status: 400 }) + } + + const amount = Number(body.amount) + if (!Number.isInteger(amount) || amount < DP_MIN_AMOUNT || amount > DP_MAX_AMOUNT) { + return Response.json({ success: false, error: 'invalidAmount' }) + } + + const points = amount * PD_PER_UNIT + const site = process.env.SITE_URL || '' + const ip = (request.headers.get('x-forwarded-for') || '').split(',')[0].trim() || '0.0.0.0' + const currency = process.env.DP_CURRENCY || 'eur' + + if (provider === 'stripe') { + const r = await createCheckoutSession({ + accountId: session.accountId, + username: session.username, + email: session.bnetEmail || '', + acoreIp: ip, + amount, + currency, + characterName: session.username, + productName: `${points} PD`, + successUrl: `${site}/d-points-success?provider=stripe`, + cancelUrl: `${site}/d-points`, + metadata: { + service: 'dpoints', + accountId: String(session.accountId), + points: String(points), + amount: String(amount), + }, + }) + if (!r.success || !r.url) return Response.json({ success: false, error: 'stripeError' }) + return Response.json({ success: true, url: r.url }) + } + + // provider === 'sumup' + if (!sumupConfigured()) return Response.json({ success: false, error: 'notConfigured' }) + const reference = randomUUID() + const r = await createSumUpCheckout({ + accountId: session.accountId, + username: session.username, + email: session.bnetEmail || '', + acoreIp: ip, + amount, + points, + reference, + description: `${points} PD`, + returnUrl: `${site}/d-points-success?provider=sumup&ref=${reference}`, + }) + if (!r.success || !r.url) return Response.json({ success: false, error: r.error || 'sumupError' }) + return Response.json({ success: true, url: r.url }) +} diff --git a/web-next/app/globals.css b/web-next/app/globals.css index 78cc6b3..a20e6e3 100644 --- a/web-next/app/globals.css +++ b/web-next/app/globals.css @@ -419,3 +419,11 @@ textarea:focus { .class-warlock { color: #9482c9; } .class-druid { color: #ff7d0a; } .class-unknown { color: #f0b100; } + +/* Logos en línea de las pasarelas de pago (lista de métodos en /d-points) */ +.pay-inline-logo { + height: 18px; + width: auto; + vertical-align: middle; + margin: 0 2px; +} diff --git a/web-next/components/DPointsTabs.tsx b/web-next/components/DPointsTabs.tsx index 4cdd093..8e25728 100644 --- a/web-next/components/DPointsTabs.tsx +++ b/web-next/components/DPointsTabs.tsx @@ -1,23 +1,12 @@ 'use client' -import { useEffect, useState } from 'react' +import { useState } from 'react' import { Link } from '@/i18n/navigation' -const IMG = '/nw-themes/nw-ryu/nw-images' -const LOGOS = `${IMG}/nw-logos` -const RANKS = `${IMG}/nw-ranks` -const FLAGS = `${IMG}/nw-country-flags` +const LOGOS = '/nw-themes/nw-ryu/nw-images/nw-logos' +const RANKS = '/nw-themes/nw-ryu/nw-images/nw-ranks' -type TabId = 'Info' | 'PayPal' | 'dLocal' | 'Crypto' | 'Skrill' | 'Argentina' | 'Chile' | 'Colombia' | 'Perú' - -// Las banderas son .png sólo para México y Panamá; el resto son .webp. -function flagSrc(code: string) { - const ext = code === 'mx' || code === 'pa' ? 'png' : 'webp' - return `${FLAGS}/${code}.${ext}` -} -function Flag({ code, name }: { code: string; name: string }) { - return {name} -} +type TabId = 'Info' | 'Stripe' | 'SumUp' const RANK_ROWS = [ ['1_Newbie', 'Nivel 1', 'Desde 0 PD'], @@ -36,23 +25,10 @@ const RANK_ROWS = [ ['14_GrandMaster', 'Nivel 14', 'Más de 10000 PD'], ] -export function DPointsTabs({ - realm, - username, - ip, - locale, - origin, - paypalBusiness, -}: { - realm: string - username: string - ip: string - locale: string - origin: string - paypalBusiness: string -}) { +export function DPointsTabs({ realm, currency }: { realm: string; currency: string }) { const [active, setActive] = useState('Info') const [showRanks, setShowRanks] = useState(false) + const sym = currency === 'usd' ? '$' : '€' return ( <> @@ -68,30 +44,11 @@ export function DPointsTabs({ Regresar
- - - - - - - -
@@ -132,682 +89,199 @@ export function DPointsTabs({
¿Cuáles son los métodos de compra de PD?

Los métodos de compra disponibles actualmente son:

-

- PayPal (Para todos los países)

-

- dLocal Go para Argentina , Bolivia , Chile , Colombia , Costa Rica , Ecuador , Guatemala , México , Panamá , Perú , Paraguay , Uruguay .

-

- Criptomonedas (Para todos los países)

-

- Skrill (Para todos los países)

-

- Métodos alternativos sólo para Argentina ( ARG)

-

- Métodos alternativos sólo para Chile ( CHI)

-

- Métodos alternativos sólo para Colombia ( COL)

-

- Métodos alternativos sólo para Perú ( PER)

-
-

Puedes hacer una sugerencia sobre nuevos métodos en nuestro foro de sugerencias aquí.

+

- Stripe Stripe (tarjeta de crédito/débito, para todos los países).

+

- SumUp SumUp (tarjeta de crédito/débito).




¿Cuántos PD se reciben? -

Cada método indicará cuántos PD se reciben.

+

Por cada {sym}1 recibes 100 PD. La cantidad final se indica antes de confirmar el pago.




¿Tienes dudas o problemas con una compra?

No realices una compra hasta estar completamente seguro de que has leído esta información y sabes cómo es el proceso que vayas a escoger.

-

Si a pesar de leer este instructivo o el de alguna de nuestras herramientas, aún tienes dudas, el equipo de {realm} podrá asesorarte en cualquier momento. Puedes colocar un ticket en el servidor, o contactarnos mediante nuestra plataforma de Discord.

+

Si a pesar de leer este instructivo aún tienes dudas, el equipo de {realm} podrá asesorarte en cualquier momento. Puedes colocar un ticket en el servidor, o contactarnos mediante nuestra plataforma de Discord.

)} - {active === 'PayPal' && ( - - )} - {active === 'dLocal' && } - {active === 'Crypto' && } - {active === 'Skrill' && } - {active === 'Argentina' && } - {active === 'Chile' && } - {active === 'Colombia' && } - {active === 'Perú' && } + {active === 'Stripe' && } + {active === 'SumUp' && } ) } -/* ---------- Nota común para pasarelas automáticas sin backend configurado ---------- */ -function gatewayUnavailable(realm: string) { - alert(`Este método de pago aún no está disponible en ${realm}. Por favor, inténtalo de nuevo más tarde o utiliza otro método.`) +/* --------------------------- Lógica de pago común --------------------------- */ +function checkoutError(error?: string): string { + switch (error) { + case 'notConfigured': + return 'Este método de pago aún no está disponible. Por favor, utiliza otro método o inténtalo más tarde.' + case 'invalidAmount': + return 'La cantidad debe ser un número entero entre 1 y 200.' + case 'notAuthenticated': + return 'Tu sesión ha expirado. Inicia sesión de nuevo.' + default: + return 'No se pudo iniciar el pago. Inténtalo de nuevo.' + } } -/* ---------------------------------- PayPal ---------------------------------- */ -function PayPalTab({ - realm, - username, - ip, - origin, - locale, - paypalBusiness, -}: { - realm: string - username: string - ip: string - origin: string - locale: string - paypalBusiness: string -}) { +function usePayment(provider: 'stripe' | 'sumup') { + const [amount, setAmount] = useState('') const [accepted, setAccepted] = useState(false) - const configured = Boolean(paypalBusiness) + const [busy, setBusy] = useState(false) + const [err, setErr] = useState(null) - function onSubmit(e: React.FormEvent) { - if (!configured) { - e.preventDefault() - gatewayUnavailable(realm) + async function pay(e: React.FormEvent, sym: string) { + e.preventDefault() + if (!accepted) { + alert('Debes aceptar los Términos y Condiciones y la Política de Reembolso') return } - const form = e.currentTarget - const amount = (form.elements.namedItem('amount') as HTMLInputElement)?.value - if (!window.confirm(`Por favor revisa que el valor en USD que has ingresado es correcto.\nEl valor ingresado es el costo final en USD que pagarás al ser redirigido.\n\nEl monto a pagar es ${amount} USD.`)) { - e.preventDefault() + const n = Number(amount) + if (!Number.isInteger(n) || n < 1 || n > 200) { + setErr(checkoutError('invalidAmount')) + return + } + if (!window.confirm(`Vas a adquirir ${n * 100} PD por ${sym}${n}.\n¿Deseas continuar al pago?`)) return + setBusy(true) + setErr(null) + try { + const res = await fetch('/api/dpoints/checkout', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + credentials: 'same-origin', + body: JSON.stringify({ provider, amount: n }), + }) + const d: { success?: boolean; url?: string; error?: string } = await res.json() + if (d.success && d.url) { + window.location.href = d.url + return + } + setErr(checkoutError(d.error)) + setBusy(false) + } catch { + setErr(checkoutError()) + setBusy(false) } } - return ( -
-
- ¿Qué es PayPal?PayPal -

Un servicio que te permite pagar, enviar dinero y aceptar pagos sin tener que introducir tus datos financieros continuamente. Paga con PayPal de forma más rápida y segura. 250 millones de personas de más de 200 países y mercados usan PayPal. Y en 25 divisas.
Más información sobre PayPal aquí.

-
-
-
- ¿Cómo adquirir PD a través de PayPal? -

Al ser un sistema auto-gestionado, las adquisiciones mediante este sistema son automáticas y en cuestión de minutos tras realizar tu compra, recibirás los PD en tu cuenta en la página web.

-
-
-
-

- Introduce en el campo de texto que aparece más abajo (el recuadro negro), la cantidad que deseas adquirir en USD.

-

- Al presionar el botón ADQUIRIR PD, serás redirigido a la plataforma de PayPal donde tendrás que ingresar con tu cuenta de PayPal y confirmar la compra en su plataforma.

-

- Una vez que PayPal valide tu pago, tendrás los PD en tu cuenta.

-
-

Por favor ten en cuenta que a la hora de realizar tu compra, la cantidad a adquirir sólo acepta números enteros y el mínimo posible es $1 USD.

-

Ejemplos de valor incorrecto: $5,90 | $22,30 | $53,10

-

Ejemplos de valor correcto: $6 | $22 | $53

-


-

$1 USD = 100 PD

-

Valor máximo por transacción: $200

-
-
-
-

¡Importante!

-

No añadas ceros decimales, sólo valores enteros.

-

Ejemplo: Si deseas 200 PD, escribe sólo el número 2, sin ceros.

-
-
-
-

Cantidad en USD a adquirir

-
- - USD - - - - - - - - - -

- setAccepted(e.target.checked)} required /> - -

- -
-
-
- ) + return { amount, setAmount, accepted, setAccepted, busy, err, pay } } -/* ---------------------------------- dLocal ---------------------------------- */ -function DLocalTab({ username, realm }: { username: string; realm: string }) { - const [accepted, setAccepted] = useState(false) +/* -------------------------------- Formulario -------------------------------- */ +function AmountForm({ + provider, + sym, + buttonLabel, +}: { + provider: 'stripe' | 'sumup' + sym: string + buttonLabel: string +}) { + const st = usePayment(provider) + const points = Number(st.amount) > 0 ? Number(st.amount) * 100 : 0 return ( -
-
- ¿Qué es dLocal Go?dLocal Go -

dLocal Go es una plataforma de pagos que permite una conexión en directo con todas las tarjetas de crédito y débito, pagos en efectivo y transferencias bancarias en diferentes países de América Latina.
Más información sobre dLocal Go aquí.

-
-
-
- ¿Cómo adquirir PD a través de dlocal Go? -

Al ser un sistema auto-gestionado, las adquisiciones mediante este sistema son automáticas y tras realizar tu compra, recibirás los PD en tu cuenta en la página web.

-
-

Tiempo aproximado de procesamiento de pagos según método:

-

- Tarjeta de crédito/debito: De minutos a 1 día hábil

-

- Efectivo: 1 a 2 días hábiles

-

- Transferencia bancaria: 1 día hábil

+ <> +

{sym}1 = 100 PD

+

Valor mínimo: {sym}1 · Valor máximo por transacción: {sym}200

+
+
+
+

¡Importante!

+

Introduce sólo valores enteros (sin decimales).

+

Ejemplo: para 200 PD escribe 2.

+
+
+

Cantidad en {sym === '$' ? 'USD' : 'EUR'} a adquirir

+
st.pay(e, sym)}> + st.setAmount(e.target.value)} + required + />{' '} + {sym === '$' ? 'USD' : 'EUR'} + {points > 0 &&

Recibirás {points} PD

}

-

Importante:

-

Los tiempos indicados en la tabla son orientativos. En algunas ocasiones, la demora puede ser mayor.

-

Ante cualquier duda con la demora, por favor contáctanos en nuestra plataforma de Discord o a través de los medios de contacto disponibles en Ayuda.

+ st.setAccepted(e.target.checked)} + required + /> + +

+ {st.err &&

{st.err}

} + +
+ + ) +} + +/* ---------------------------------- Stripe ---------------------------------- */ +function StripeTab({ realm, sym }: { realm: string; sym: string }) { + return ( +
+
+ ¿Qué es Stripe?Stripe +

Stripe es una plataforma de pagos en línea segura que procesa tarjetas de crédito y débito de todo el mundo. Tus datos de pago se gestionan directamente en Stripe; {realm} nunca los almacena.
Más información sobre Stripe aquí.




-

- Introduce en el campo de texto que aparece más abajo (el recuadro negro), la cantidad que deseas adquirir en USD.

-

- Al presionar el botón ADQUIRIR PD, serás redirigido a la plataforma de dLocal Go donde tendrás que realizar el pago y completar la compra en su plataforma.

-

- Una vez que dLocal Go valide tu pago, tendrás los PD en tu cuenta.

+ ¿Cómo adquirir PD a través de Stripe? +

Al ser un sistema auto-gestionado, las adquisiciones son automáticas y en cuestión de minutos tras completar el pago recibirás los PD en tu cuenta.


-

Por favor ten en cuenta que a la hora de realizar tu compra, la cantidad a adquirir sólo acepta números enteros y el mínimo posible es $2 USD.

-

Ejemplos de valor incorrecto: $5,90 | $22,30 | $53,10

-

Ejemplos de valor correcto: $6 | $22 | $53

+
+
+

- Introduce en el campo de más abajo la cantidad que deseas adquirir.

+

- Al presionar ADQUIRIR PD, serás redirigido a la pasarela segura de Stripe para completar el pago con tu tarjeta.

+

- Una vez que Stripe valide tu pago, tendrás los PD en tu cuenta de forma automática.

+
+

Recuerda que la cantidad sólo acepta números enteros y el mínimo posible es {sym}1.

+

Ejemplos de valor incorrecto: {sym}5,90 | {sym}22,30 | {sym}53,10

+

Ejemplos de valor correcto: {sym}6 | {sym}22 | {sym}53




-

$1 USD = 100 PD

-

Valor mínimo por transacción: $2

-
-

Cantidad en USD a adquirir

-
{ e.preventDefault(); if (!accepted) { alert('Debes aceptar los Términos y Condiciones y la Política de Reembolso'); return } gatewayUnavailable(realm) }}> - USD - -

- setAccepted(e.target.checked)} required /> - -

- -
+
) } -/* ---------------------------------- Crypto ---------------------------------- */ -function CryptoTab({ username, realm }: { username: string; realm: string }) { - const [accepted, setAccepted] = useState(false) +/* ---------------------------------- SumUp ----------------------------------- */ +function SumUpTab({ realm, sym }: { realm: string; sym: string }) { return (
- ¿Qué son las criptomonedas?NOWPayments -

Las criptomonedas son monedas digitales descentralizadas que permiten realizar pagos de forma rápida, segura y sin intermediarios. Aceptamos más de 150 criptomonedas a través de NOWPayments.

-

Más información sobre NOWPayments aquí.

+ ¿Qué es SumUp?SumUp +

SumUp es una plataforma de pagos europea que permite pagar de forma rápida y segura con tarjeta de crédito o débito. Tus datos de pago se gestionan directamente en SumUp; {realm} nunca los almacena.
Más información sobre SumUp aquí.




- ¿Cómo adquirir PD con criptomonedas? -

Al ser un sistema auto-gestionado, las adquisiciones mediante este sistema son automáticas y tras realizar tu compra, recibirás los PD en tu cuenta en la página web.

-
-

Tiempo aproximado de procesamiento según criptomoneda:

-

- BNB, USDT (BSC), USDC (BSC): 1-5 minutos

-

- ETH (Base), USDC (Base): 1-5 minutos

-

- Bitcoin (BTC): 10-60 minutos

-
-

-

Importante:

-

Los tiempos indicados son orientativos y dependen de la congestión de la red blockchain.

-

Ante cualquier duda, por favor contáctanos en nuestra plataforma de Discord o a través de los medios de contacto disponibles en Ayuda.

+ ¿Cómo adquirir PD a través de SumUp? +

Al ser un sistema auto-gestionado, las adquisiciones son automáticas y tras completar el pago recibirás los PD en tu cuenta.




-

- Introduce en el campo de texto la cantidad que deseas adquirir en USD.

-

- Al presionar el botón ADQUIRIR PD, serás redirigido a la plataforma de NOWPayments donde podrás elegir la criptomoneda con la que deseas pagar.

-

- Una vez que NOWPayments confirme tu pago en la blockchain, tendrás los PD en tu cuenta.

+

- Introduce en el campo de más abajo la cantidad que deseas adquirir.

+

- Al presionar ADQUIRIR PD, serás redirigido a la pasarela segura de SumUp para completar el pago con tu tarjeta.

+

- Una vez que SumUp valide tu pago, tendrás los PD en tu cuenta de forma automática.


-

Por favor ten en cuenta que la cantidad a adquirir sólo acepta números enteros y el mínimo posible es $2 USD.

-

Ejemplos de valor incorrecto: $5,90 | $22,30 | $53,10

-

Ejemplos de valor correcto: $6 | $22 | $53

+

Recuerda que la cantidad sólo acepta números enteros y el mínimo posible es {sym}1.

+

Ejemplos de valor incorrecto: {sym}5,90 | {sym}22,30 | {sym}53,10

+

Ejemplos de valor correcto: {sym}6 | {sym}22 | {sym}53




-

$1 USD = 100 PD

-

Valor mínimo por transacción: $2 | Valor máximo: $200

-
-
-
-

¡Importante!

-

Envía el monto EXACTO que indica NOWPayments.

-

Si envías menos, el pago quedará pendiente y deberás contactar a soporte.

-
-
-
-

Cantidad en USD a adquirir

-
{ e.preventDefault(); if (!accepted) { alert('Debes aceptar los Términos y Condiciones y la Política de Reembolso'); return } gatewayUnavailable(realm) }}> - USD - -

- setAccepted(e.target.checked)} required /> - -

- -
-
-
- ) -} - -/* ---------------------------------- Skrill ---------------------------------- */ -function SkrillTab() { - return ( -
-
- ¿Qué es Skrill?Skrill -

Skrill es una plataforma de pagos en línea rápida y segura. Envía dinero, paga y recibe pagos sin ingresar tus datos financieros constantemente. Presente en 200 países y acepta 40 monedas.

-

Más información sobre Skrill aquí.

-
-
-
- ¿Cuál es el valor de los PD? -

El valor será calculado según país en base al cambio indicado en el momento del envío.

-
-

$1 USD = 100 PD

-
-
-
- ¿Cómo adquirir PD por Skrill? -

Paso 1) Inicia sesión en Skrill. Accede a tu cuenta desde la aplicación o la página web oficial de Skrill.

-
-

Paso 2) Envía el dinero

-

1. Selecciona la opción "Enviar" o "Send Money".

-

2. Elige "Skrill a Skrill".

-

3. Introduce el correo del destinatario: skrill@ultimowow.com

-

4. Indica el importe que deseas enviar.

-

5. Revisa los datos y confirma la operación.

-
-

Paso 3) Guarda el comprobante

-

Una vez completado el pago, realiza una captura de pantalla donde se vea claramente:

-

- El correo del destinatario.

-

- El importe enviado.

-

- El estado de la transacción (Completada, Exitosa o similar).

-
-

Paso 4) Envía la información

-

Envía un correo a skrill@ultimowow.com indicando:

-

- El nombre de cualquier personaje de la cuenta donde deseas recibir los PD.

-

- La captura de pantalla de la transacción. No envíes el comprobante en formato PDF.

-
-

Paso 5) Recepción de PD

-

Una vez verificado el pago, los PD serán acreditados en tu cuenta lo antes posible. Cuando la entrega haya sido completada, serás informado.

-
-
-
- Contacto -

Correo: skrill@ultimowow.com

-
-

Este método no es automático sino manual siendo realizado por la persona encargada.

-

Recomendamos no realizar ninguna acción sin comprobar primero si la persona encargada está disponible para entregar los PD.

-
-
- ) -} - -/* -------------------------------- Argentina --------------------------------- */ -function ArgentinaTab() { - const [usd, setUsd] = useState(1) - const [rate, setRate] = useState(null) - - useEffect(() => { - let alive = true - fetch('https://dolarapi.com/v1/dolares/oficial') - .then((r) => (r.ok ? r.json() : Promise.reject(new Error('HTTP ' + r.status)))) - .then((j) => { if (alive) setRate(typeof j?.venta === 'number' ? j.venta : null) }) - .catch(() => {}) - return () => { alive = false } - }, []) - - const fmtARS = (v: number) => v.toLocaleString('es-AR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) - const validUsd = Number.isInteger(usd) && usd >= 1 - const arsText = rate && validUsd ? fmtARS(rate * usd) : '—' - const pdText = validUsd ? String(usd * 100) : '—' - - return ( -
-
- ¿Qué son los métodos alternativos para Argentina? -

Son métodos disponibles para Argentina para aquellos que no tienen acceso a PayPal.

-

Este método trabaja con Pesos Argentinos.

-
-
-
- Métodos disponibles -

- Transferencia bancaria en pesos argentinos (ARS)

-
-
-
- ¿Cuál es el valor de los PD? -

La cantidad de Pesos según país será en base al cambio indicado en el momento de la transferencia.

-

El siguiente recuadro permite hacer una conversión de USD a Pesos argentinos mostrando el valor final y no habrá impuestos ni recargos.

-

Se puede cambiar los valores para ver el precio de la cantidad deseada de PD.

-
-

- Calculadora de conversión -

- setUsd(parseInt(e.target.value, 10))} - style={{ width: '60px', textAlign: 'center' }} - />{' '} - USD ={' '} - {arsText} ARS ={' '} - {pdText} PD -

-
-
-
- ¿Cómo adquirir PD por Transferencia? -

Paso 1) Realiza la transferencia con los siguientes datos:

-

   El valor en ARS puede obtenerse usando la calculadora disponible.

-

   Ejemplo: Si deseas 200 PD, coloca 2 en la casilla USD y te dirá el monto en ARS

-

   Envía el monto exacto que indica la calculadora.

-
-

Paso 2) Envia el correo detallando:

-

 - Nombre de cualquier PERSONAJE de la cuenta donde deseas recibir los PD.

-

 - Cantidad de PD deseada.

-

Paso 3) Adjunta al correo una captura de pantalla (imagen, no pdf) del comprobante.

-

Paso 4) Envia el correo a la dirección indicada en Contacto.

-

Paso 5) Espera la respuesta al correo enviado.

-

Paso 6) Dentro del horario de atención, serás informado que has recibido los PD.

-
-
-
- Contacto -

Correo: arg@ultimowow.com

-

Lunes a Viernes por las tardes (Horario Argentina).

-

-

Este método no es automático sino manual siendo realizado por la persona encargada.

-

Recomendamos no realizar ninguna acción sin comprobar primero si la persona encargada está disponible para entregar los PD.

-
-
- ) -} - -/* ----------------------------------- Chile ---------------------------------- */ -function ChileTab() { - const rows: [string, string][] = [ - ['1.200 Pesos Chilenos', '100 PD'], - ['3.600 Pesos Chilenos', '300 PD'], - ['6.000 Pesos Chilenos', '500 PD'], - ['9.600 Pesos Chilenos', '800 PD'], - ['12.000 Pesos Chilenos', '1000 PD'], - ['30.000 Pesos Chilenos', '2500 PD'], - ['60.000 Pesos Chilenos', '5000 PD'], - ] - return ( -
-
- ¿Qué son los métodos alternativos para Chile? -

Son métodos disponibles para Chile para aquellos que no tienen acceso a PayPal.

-

Este método trabaja con Pesos Chilenos.

-
-
-
- Métodos disponibles -

- Transferencia bancaria en pesos chilenos (CLP)

-

- Mach en pesos chilenos (CLP)

-
-
-
- ¿Cuál es el valor de los PD? - - - - {rows.map(([p, pd]) => ())} - -
PrecioPD
{p}{pd}
-
-
-
- ¿Cómo adquirir PD por Transferencia? -

Paso 1) Contacta con la persona encargada mediante Discord. El medio de contacto aparece al final de esta sección.

-

Paso 2) Debes informarle:

-

- Nombre de cualquier personaje de la cuenta donde deseas recibir los PD.

-

- Cantidad de PD deseada.

-

Paso 3) La persona encargada te dará los datos necesarios para la transferencia.

-

Paso 4) Confirma con la persona encargada el costo final antes de realizar la transferencia.

-

Si usas el método Mach, se te entregará un código QR para realizar la transferencia.

-

Paso 5) Una vez realizada la transferencia, deberás enviarla como imagen (no como archivo pdf) a la persona encargada.

-

Paso 6) Serás informado que has recibido los PD.

-
-
-
- Contacto -

- Discord al usuario Bhaal (ID dakiron)

-
- -

-

* Asegúrate que la persona que contactes tiene rango Maestro de Juego en nuestro discord.

-
-

Este método no es automático sino manual siendo realizado por la persona encargada.

-

Recomendamos no realizar ninguna acción sin comprobar primero si la persona encargada está disponible para entregar los PD.

-
-
- ) -} - -/* --------------------------------- Colombia --------------------------------- */ -function ColombiaTab() { - const rows: [string, string][] = [ - ['17.200 Pesos Colombianos', '300 PD'], - ['23.000 Pesos Colombianos', '400 PD'], - ['28.700 Pesos Colombianos', '500 PD'], - ['34.400 Pesos Colombianos', '600 PD'], - ['57.350 Pesos Colombianos', '1000 PD'], - ['143.350 Pesos Colombianos', '2500 PD'], - ] - return ( -
-
- ¿Qué son los métodos alternativos para Colombia? -

Son métodos disponibles para Colombia para aquellos que no tienen acceso a PayPal que se lleva a cabo a través de un representante oficial, a través de diversos métodos de pago para un país en específico.

-

Este método trabaja con Pesos Colombianos.

-
-
-
- ¿Cuál es el valor de los PD? - - - - {rows.map(([p, pd]) => ())} - -
PrecioPD
{p}{pd}
-
-
-
- ¿Cómo adquiir PD por Representante? -

Para realizar una adquisición y recibir tus PD, tienes varios medios disponibles:

-

- Recarga NEQUI en Efectivo

-

- Recarga NEQUI mediante PSE

-

- Transferencia mediante NEQUI

-

- Consignación o transferencia bancaria

-
-
- Por Recarga NEQUI en EfectivoNequi -
-

1 - Ubica el punto más cercano a donde estés aquí.

-

2 - Al entrar te solicitará conocer tu ubicación y debes hacer clic al botón Permitir.

-

3 - Cuando la web te pregunte ¿Qué quieres hacer? Selecciona la opción Recargar.

-

4 - Mira las ubicaciones que te aparecen señalados en el mapa en color amarillo (Para ver la dirección exacta toca el punto que deseas ver en el mapa).

-

5 - Ve al establecimiento elegido e indica que quieres Recargar a NEQUI X Pesos donde X es la cantidad de pesos correspondiente a los PD deseados (Verifica la tabla y coloca el valor correctamente).

-

6 - Necesitarás dar la siguiente información:

-
-
    -
  • Número de convenio: 63703
  • -
  • Número de celular: 3133639892
  • -
-
-

7 - Solicita recibo de la transacción.

-

8 - Escanea o toma una foto de dicho recibo y envíalo al representante junto con la cantidad de PD y el nombre del personaje donde deseas recibir los PD.

-

9 - Espera a que el encargado te envíe respuesta por el medio que le enviaste el mensaje donde te indicará que ya fueron cargados los PD.

-
-
-
-
- Por Recarga NEQUI mediante PSENequi -
-

El recargar NEQUI por PSE es un método que permite enviar dinero de una cuenta bancaria (de casi cualquier banco) a NEQUI.

-

Nota: Este método puede implicar que tengas activo algunos servicios en el banco donde tienes tu cuenta para poder realizarlo.

-
-

1 - Ingresa a la web de Nequi.

-

2 - Haz clic en la opción Recarga.

-

3 - Aparecerá un formulario que solicitará la siguiente información:

-
-
    -
  • Número de celular: 3133639892
  • -
  • Confirma el número de celular: 3133639892
  • -
  • Tipo de persona: Natural
  • -
  • Cantidad: Cantidad correspondiente a los PD deseados (Verifica la tabla y coloca correctamente el valor)
  • -
-
-

4 - Haz clic en No soy robot y de ahí a Continuar.

-

5 - Al momento de dar a Continuar serás redireccionado a la web oficial de PSE, donde deberás ingresar TU cuenta de PSE (si nunca antes haz hecho pagos por PSE deberás registrarte).

-

6 - Cuando ingreses la cuenta de PSE serás redireccionado a la web de tu banco que accede a este servicio donde deberás completar el proceso que tenga el banco de TU cuenta bancaria (puede que algunos bancos cobren adicional por el acceso o uso de este servicio).

-

7 - Una vez completado y finalizado el pago por PSE, serás re direccionado a NEQUI donde te mostrará un enlace con el mensaje Ver comprobante al cual debes hacerle clic.

-

8 - Descarga el comprobante de pago para generar un archivo en formato pdf y envíalo al representante junto con la cantidad de PD y el nombre del personaje donde deseas recibir los PD.

-

9 - Espera a que el encargado te envíe respuesta por el medio que le enviaste el mensaje donde te indicará que ya fueron cargados los PD.

-
-
-
-
- Transferencia mediante NEQUINequi -
-

1 - Ingresa a tu cuenta NEQUI Desde la App móvil de NEQUI Colombia.

-

2 - Al ingresar, selecciona el botón con el símbolo $ de color rosa.

-

3 - Selecciona la opción Envía plata.

-

4 - Indica desde dónde enviarás el dinero.

-

5 - En los campos que están en Envía a NEQUI coloca la siguiente información:

-
-
    -
  • Número de celular: 3133639892
  • -
  • Cantidad: Cantidad correspondiente a los PD deseados (Verifica la tabla y coloca correctamente el valor)
  • -
  • Mensaje: PD UltimoWoW
  • -
-
-

6 - Presiona Continuar.

-

7 - Una vez que se termine de procesar el pago te mostrará la lista de tus movimientos, en el último sale el nombre de la persona relacionada a la cuenta transferida que es ROBERT PENA.

-

8 - Selecciona dicho movimiento para que te muestre los detalles del mismo.

-

9 - Toma una captura de pantalla de los detalles y envíalo al representante junto con la cantidad de PD y el nombre del personaje donde deseas recibir los PD.

-

10 - Espera a que el encargado te envíe respuesta por el medio que le enviaste el mensaje donde te indicará que ya fueron cargados los PD.

-
-
-
-
- Consignación o Transferencia a cuenta bancariaBancolombia -
-

Mediante este método deberas realizar una consignación en un banco o corresponsal bancario por el valor de los PD que quieres adquirir (Verifica la tabla y coloca correctamente el valor).

-
-

1 - Cuando realices la consignación te solicitarán los siguientes datos:

-
-
    -
  • Número de cuenta: 203-133849-78
  • -
  • A nombre de: Robert Camilo Peña
  • -
  • Tipo de cuenta: De ahorros
  • -
  • Banco: Bancolombia
  • -
-
-

2 - Solicita copia del recibo de transacción.

-

3 - Escanea o toma una foto del recibo y envíalo al representante junto con la cantidad de PD y el nombre del personaje donde deseas recibir los PD.

-

4 - Espera a que el encargado te envíe respuesta por el medio que le enviaste el mensaje donde te indicará que ya fueron cargados los PD.

-
-
-
-
-
- ¿Cómo contactar con el Representante? -

Nombre: Robert Peña

-

Usuario de Discord: Raziel#1403

-

Lunes a Jueves: 15:00 a 22:00 (Horario Colombia)

-

Sábados y Domingos: 18:00 a 23:00 (Horario Colombia)

-

Importante: Los días Viernes no hay atención

-
- - -
-
-
- ) -} - -/* ----------------------------------- Perú ----------------------------------- */ -function PeruTab() { - const rows: [string, string][] = [ - ['5.5 Soles Peruanos', '100 PD'], - ['25,5 Soles Peruanos', '500 PD'], - ['40 Soles Peruanos', '800 PD'], - ['48 Soles Peruanos', '1000 PD'], - ['118 Soles Peruanos', '2500 PD'], - ] - return ( -
-
- ¿Qué son los métodos alternativos para Perú? -

Son métodos disponibles para aquellos que no tienen acceso a PayPal que se lleva a cabo a través de un representante oficial, a través de diversos métodos de pago para un país en específico.

-

Este método trabaja con Soles Peruanos.

-
-
-
- ¿Cuál es el valor de los PD? - - - - {rows.map(([p, pd]) => ())} - -
PrecioPD
{p}{pd}
-
-
-
- ¿Cómo adquirir PD por Representante? -

Para realizar una adquisición y recibir tus PD, tienes varios medios disponibles:

-

- Recarga por medio de YAPE

-

- Recarga por medio de PLIN

-

- Pago presencial mediante OFICINA (LIMA)

-

- Transferencia bancaria y interbancaria

-
-
- Recarga por medio de YAPE -
- Yape -
-
-
-
- Pago por medio de PLIN -
-

Pago mediante número.

-
- Plin -
-
-
-
- Pago presencial mediante OFICINA -
-

Contactarse con el número mediante 941841649 previamente cita.

-
- Oficina -
-
-
-
- Transferencia bancaria y interbancaria -
-

Mediante este método deberas realizar una consignación en un banco o corresponsal bancario por el valor de los PD que quieres adquirir (Verifica la tabla y coloca correctamente el valor).

-
-

- BANCO BCP

-

- BANCO BBVA

-

- BANCO SCOTIABANK

-

- BANCO DE LA NACIÓN

-
-

Comunicarse previamente con el representante.

-
-
-
-

Una vez realizado el proceso por cualquiera de los medios indicados, contactar con el Representante.

-

Si es en los horarios de atención, los PD serán cargados en minutos en la cuenta.

-
-
-
- ¿Cómo contactar con el Representante? -

Nombre: Anibal Gavidia Correa

-

Lunes a Jueves: 9:00 a 22:00 (Horario Perú)

-

Sábados y Domingos: 9:00 a 23:00 (Horario Perú)

-
- - -
+
) diff --git a/web-next/lib/dpoints.ts b/web-next/lib/dpoints.ts new file mode 100644 index 0000000..88e6bea --- /dev/null +++ b/web-next/lib/dpoints.ts @@ -0,0 +1,27 @@ +import type { RowDataPacket } from 'mysql2' +import { db, DB } from './db' + +/** Ratio de conversión: cada 1 unidad de moneda = 100 PD. */ +export const PD_PER_UNIT = 100 +/** Límites de compra por transacción (en unidades de moneda). */ +export const DP_MIN_AMOUNT = 1 +export const DP_MAX_AMOUNT = 200 + +/** Acredita PD (donate points) a una cuenta de juego en `home_api_points` (upsert). */ +export async function creditDPoints(accountId: number, points: number): Promise { + if (!accountId || !(points > 0)) return false + try { + const [rows] = await db(DB.default).query( + 'SELECT id FROM home_api_points WHERE accountID = ?', + [accountId], + ) + if (rows[0]) { + await db(DB.default).query('UPDATE home_api_points SET dp = dp + ? WHERE accountID = ?', [points, accountId]) + } else { + await db(DB.default).query('INSERT INTO home_api_points (accountID, vp, dp) VALUES (?, 0, ?)', [accountId, points]) + } + return true + } catch { + return false + } +} diff --git a/web-next/lib/paid-services.ts b/web-next/lib/paid-services.ts index 3d5fc00..e86e7cd 100644 --- a/web-next/lib/paid-services.ts +++ b/web-next/lib/paid-services.ts @@ -1,5 +1,6 @@ import { executeSoapCommand } from './soap' import { db, DB } from './db' +import { creditDPoints } from './dpoints' import { getRenamePrice, getCustomizePrice, @@ -78,6 +79,14 @@ export const PAID_SERVICES: Record = { fulfill: (c, m) => soapFulfill(`.char changeaccount ${m.destination_account} ${c}`), extraFields: ['destination_account'], }, + // Compra de PD (Adquirir PD): acredita puntos a la cuenta que hizo el pago. + // El importe lo elige el usuario, por eso `price` lee metadata.amount. + dpoints: { + price: (m) => Promise.resolve(Number(m.amount)), + productName: (_c, m) => `${Number(m.points)} PD`, + fulfill: (_c, m) => creditDPoints(Number(m.accountId), Number(m.points)), + extraFields: [], + }, } export function getPaidService(slug: string): PaidServiceConfig | null { diff --git a/web-next/lib/sumup.ts b/web-next/lib/sumup.ts new file mode 100644 index 0000000..e3843ab --- /dev/null +++ b/web-next/lib/sumup.ts @@ -0,0 +1,108 @@ +import type { RowDataPacket, ResultSetHeader } from 'mysql2' +import { db, DB } from './db' +import { creditDPoints, PD_PER_UNIT } from './dpoints' + +const API = 'https://api.sumup.com/v0.1' + +/** SumUp está disponible sólo si hay API key y merchant code configurados. */ +export function sumupConfigured(): boolean { + return Boolean(process.env.SUMUP_API_KEY && process.env.SUMUP_MERCHANT_CODE) +} + +function authHeaders(): Record { + return { + Authorization: `Bearer ${process.env.SUMUP_API_KEY}`, + 'Content-Type': 'application/json', + } +} + +interface CreateParams { + accountId: number + username: string + email: string + acoreIp: string + amount: number // importe en la moneda del comercio + points: number + reference: string + description: string + returnUrl: string +} + +/** + * Crea un checkout hospedado de SumUp y registra el pago en `home_stripelog` + * (reutilizamos la tabla de logs; `mode = 'sumup'`, `session_id = reference`). + * Devuelve la URL del checkout hospedado para redirigir al usuario. + */ +export async function createSumUpCheckout(p: CreateParams): Promise<{ success: boolean; url?: string; error?: string }> { + if (!sumupConfigured()) return { success: false, error: 'notConfigured' } + try { + const res = await fetch(`${API}/checkouts`, { + method: 'POST', + headers: authHeaders(), + body: JSON.stringify({ + checkout_reference: p.reference, + amount: p.amount, + currency: process.env.SUMUP_CURRENCY || 'EUR', + merchant_code: process.env.SUMUP_MERCHANT_CODE, + description: p.description, + return_url: p.returnUrl, + hosted_checkout: { enabled: true }, + }), + }) + if (!res.ok) return { success: false, error: `sumup_${res.status}` } + const data: { id?: string; hosted_checkout_url?: string } = await res.json() + const url = data.hosted_checkout_url + if (!url) return { success: false, error: 'noHostedUrl' } + + await db(DB.default).query( + 'INSERT INTO home_stripelog (account_id, username, email, acore_ip, stripe_ip, product_name, amount, session_id, mode, character_name, timestamp, fulfilled) ' + + 'VALUES (?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, NOW(), 0)', + [p.accountId, p.username, p.email, p.acoreIp, p.description, p.amount, p.reference, 'sumup', p.username], + ) + return { success: true, url } + } catch (e) { + return { success: false, error: String(e) } + } +} + +/** ¿El checkout con esta referencia está PAGADO en SumUp? */ +async function isReferencePaid(reference: string): Promise { + try { + const res = await fetch(`${API}/checkouts?checkout_reference=${encodeURIComponent(reference)}`, { + headers: authHeaders(), + }) + if (!res.ok) return false + const list: { status?: string }[] = await res.json() + return Array.isArray(list) && list.some((c) => c.status === 'PAID') + } catch { + return false + } +} + +/** + * Entrega un checkout SumUp pagado: verifica el estado, reclama de forma atómica + * (fulfilled 0->1) y acredita los PD. Idempotente. Devuelve true si acreditó ahora. + */ +export async function fulfillSumUpCheckout(reference: string): Promise<{ ok: boolean; paid: boolean }> { + if (!reference) return { ok: false, paid: false } + const paid = await isReferencePaid(reference) + if (!paid) return { ok: false, paid: false } + + const [rows] = await db(DB.default).query( + 'SELECT id, account_id, amount FROM home_stripelog WHERE session_id = ? AND mode = ?', + [reference, 'sumup'], + ) + const log = rows[0] + if (!log) return { ok: false, paid: true } + + // Reclamo atómico: sólo gana quien pasa fulfilled 0->1. + const [res] = await db(DB.default).query( + 'UPDATE home_stripelog SET fulfilled = 1 WHERE id = ? AND fulfilled = 0', + [log.id], + ) + if (res.affectedRows === 0) return { ok: false, paid: true } + + const points = Math.round(Number(log.amount) * PD_PER_UNIT) + const ok = await creditDPoints(Number(log.account_id), points) + return { ok, paid: true } +} diff --git a/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/nowpayments-logo.png b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/nowpayments-logo.png deleted file mode 100644 index 23cf2cab33fe47192a5709588de52d0e1ee03272..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12640 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 bEA~j;=-=187l-(NP5?Aj^&pjCXypF`M-~M< diff --git a/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/stripe-logo.svg b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/stripe-logo.svg new file mode 100644 index 0000000..0907489 --- /dev/null +++ b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/stripe-logo.svg @@ -0,0 +1,4 @@ + + + stripe + diff --git a/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/stripe-p-logo.svg b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/stripe-p-logo.svg new file mode 100644 index 0000000..f005edd --- /dev/null +++ b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/stripe-p-logo.svg @@ -0,0 +1,4 @@ + + + S + diff --git a/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/sumup-logo.svg b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/sumup-logo.svg new file mode 100644 index 0000000..7a280ea --- /dev/null +++ b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/sumup-logo.svg @@ -0,0 +1,5 @@ + + + + SumUp + diff --git a/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/sumup-p-logo.svg b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/sumup-p-logo.svg new file mode 100644 index 0000000..5cbb775 --- /dev/null +++ b/web-next/public/nw-themes/nw-ryu/nw-images/nw-logos/sumup-p-logo.svg @@ -0,0 +1,4 @@ + + + +