chore: add crypto payment

This commit is contained in:
2026-03-18 18:41:07 +01:00
parent a82097a228
commit adf68201fd
12 changed files with 788 additions and 2 deletions
+4
View File
@@ -755,6 +755,10 @@ export interface AppSettings {
referral_enabled: boolean;
delivery_schedule: DeliverySchedule;
postal_zones: PostalZone[];
crypto_payment_enabled: boolean;
nowpayments_api_key: string;
nowpayments_ipn_secret: string;
nowpayments_currencies: string[];
}
export const getSettings = async (): Promise<{
@@ -21,6 +21,19 @@ import { useAlert } from "../../hooks/useAlert";
const POOL_COLORS = ["#10b981", "#9333ea", "#f97316", "#3b82f6", "#ef4444"];
const CRYPTO_CURRENCIES = [
{ id: "btc", label: "BTC", icon: "₿", color: "#f7931a" },
{ id: "eth", label: "ETH", icon: "Ξ", color: "#627eea" },
{ id: "ltc", label: "LTC", icon: "Ł", color: "#bebebe" },
{ id: "usdt", label: "USDT", icon: "₮", color: "#26a17b" },
{ id: "usdttrc20",label: "USDT TRC20",icon: "₮", color: "#c23631" },
{ id: "bnb", label: "BNB", icon: "B", color: "#f3ba2f" },
{ id: "sol", label: "SOL", icon: "◎", color: "#9945ff" },
{ id: "doge", label: "DOGE", icon: "Ð", color: "#c2a633" },
{ id: "trx", label: "TRX", icon: "T", color: "#c23631" },
{ id: "xmr", label: "XMR", icon: "ɱ", color: "#ff6600" },
];
const DAYS: { key: keyof DeliverySchedule; label: string }[] = [
{ key: "monday", label: "Lundi" },
{ key: "tuesday", label: "Mardi" },
@@ -641,7 +654,13 @@ export default function SettingsScreen() {
referral_enabled: true,
delivery_schedule: DEFAULT_DELIVERY_SCHEDULE,
postal_zones: DEFAULT_POSTAL_ZONES,
crypto_payment_enabled: false,
nowpayments_api_key: "",
nowpayments_ipn_secret: "",
nowpayments_currencies: [],
});
const [showApiKey, setShowApiKey] = useState(false);
const [showIpnSecret, setShowIpnSecret] = useState(false);
const [categories, setCategories] = useState<Category[]>([]);
// Refs pour l'auto-sauvegarde au départ de la page
@@ -858,6 +877,16 @@ export default function SettingsScreen() {
textAlign: "center",
},
thresholdSep: { fontSize: fontSize.md, color: colors.textMuted },
input: {
backgroundColor: colors.bgPrimary,
borderWidth: 1,
borderColor: colors.border,
borderRadius: borderRadius.sm,
paddingHorizontal: spacing.m,
paddingVertical: spacing.s,
color: colors.textPrimary,
fontSize: fontSize.sm,
},
saveButton: {
backgroundColor: colors.accent,
borderRadius: borderRadius.lg,
@@ -1124,6 +1153,129 @@ export default function SettingsScreen() {
s={s}
/>
{/* Paiement crypto */}
<View style={s.section}>
<Text style={s.sectionTitle}>Paiement crypto</Text>
{/* Toggle activation */}
<View style={[s.row, s.rowFirst]}>
<View style={s.rowLeft}>
<Text style={s.rowLabel}>Paiement crypto activé</Text>
<Text style={s.rowDesc}>
Les clients peuvent payer leurs commandes en cryptomonnaie via NowPayments.
</Text>
</View>
<Switch
value={settings.crypto_payment_enabled}
onValueChange={(v) =>
setSettings((prev) => ({ ...prev, crypto_payment_enabled: v }))
}
trackColor={{ false: colors.border, true: colors.accent }}
thumbColor="#fff"
/>
</View>
{/* Config NowPayments */}
<View
pointerEvents={settings.crypto_payment_enabled ? "auto" : "none"}
style={{ opacity: settings.crypto_payment_enabled ? 1 : 0.4, paddingHorizontal: spacing.l, paddingBottom: spacing.m, borderTopWidth: 1, borderTopColor: colors.border, paddingTop: spacing.m, gap: spacing.m }}
>
{/* Clé API */}
<View>
<Text style={[s.rowLabel, { marginBottom: spacing.xs }]}>Clé API NowPayments</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.s }}>
<TextInput
style={[s.input, { flex: 1, fontFamily: "monospace" }]}
value={settings.nowpayments_api_key}
onChangeText={(v) => setSettings((p) => ({ ...p, nowpayments_api_key: v }))}
placeholder="Votre clé API NowPayments"
placeholderTextColor={colors.textSecondary}
secureTextEntry={!showApiKey}
autoCapitalize="none"
autoCorrect={false}
/>
<TouchableOpacity onPress={() => setShowApiKey((x) => !x)} style={{ padding: spacing.xs }}>
<Ionicons name={showApiKey ? "eye-off-outline" : "eye-outline"} size={20} color={colors.textSecondary} />
</TouchableOpacity>
</View>
</View>
{/* Secret IPN */}
<View>
<Text style={[s.rowLabel, { marginBottom: spacing.xs }]}>Secret IPN</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.s }}>
<TextInput
style={[s.input, { flex: 1, fontFamily: "monospace" }]}
value={settings.nowpayments_ipn_secret}
onChangeText={(v) => setSettings((p) => ({ ...p, nowpayments_ipn_secret: v }))}
placeholder="Secret IPN pour la vérification des webhooks"
placeholderTextColor={colors.textSecondary}
secureTextEntry={!showIpnSecret}
autoCapitalize="none"
autoCorrect={false}
/>
<TouchableOpacity onPress={() => setShowIpnSecret((x) => !x)} style={{ padding: spacing.xs }}>
<Ionicons name={showIpnSecret ? "eye-off-outline" : "eye-outline"} size={20} color={colors.textSecondary} />
</TouchableOpacity>
</View>
<Text style={[s.rowDesc, { marginTop: spacing.xs }]}>
URL IPN à configurer sur NowPayments :{"\n"}
<Text style={{ color: colors.accent, fontSize: fontSize.xs }}>
https://5.181.0.112.nip.io/api/v1/webhooks/nowpayments
</Text>
</Text>
</View>
{/* Cryptos acceptées */}
<View>
<Text style={[s.rowLabel, { marginBottom: spacing.xs }]}>Cryptomonnaies acceptées</Text>
<Text style={[s.rowDesc, { marginBottom: spacing.s }]}>
Sélectionnez les cryptos que vos clients peuvent utiliser pour payer.
</Text>
<View style={{ flexDirection: "row", flexWrap: "wrap", gap: spacing.s }}>
{CRYPTO_CURRENCIES.map((coin) => {
const selected = (settings.nowpayments_currencies ?? []).includes(coin.id);
return (
<TouchableOpacity
key={coin.id}
onPress={() => {
setSettings((prev) => {
const current = prev.nowpayments_currencies ?? [];
const updated = selected
? current.filter((c) => c !== coin.id)
: [...current, coin.id];
return { ...prev, nowpayments_currencies: updated };
});
}}
style={{
flexDirection: "row",
alignItems: "center",
gap: spacing.xs,
paddingHorizontal: spacing.m,
paddingVertical: spacing.s,
borderRadius: borderRadius.full,
borderWidth: 1.5,
borderColor: selected ? coin.color : colors.border,
backgroundColor: selected ? coin.color + "22" : colors.bgSecondary,
}}
>
<Text style={{ fontSize: 16 }}>{coin.icon}</Text>
<Text style={{ fontSize: fontSize.sm, fontWeight: "600", color: selected ? coin.color : colors.textSecondary }}>
{coin.label}
</Text>
</TouchableOpacity>
);
})}
</View>
{(settings.nowpayments_currencies ?? []).length === 0 && (
<Text style={[s.rowDesc, { color: "#ef4444", marginTop: spacing.s }]}>
Sélectionnez au moins une cryptomonnaie.
</Text>
)}
</View>
</View>
</View>
<TouchableOpacity
style={s.saveButton}
onPress={handleSave}