recharge.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import Alipay from "expo-native-alipay";
  2. import { Stack, useRouter } from "expo-router";
  3. import React, { useState } from "react";
  4. import {
  5. ActivityIndicator,
  6. Alert,
  7. StyleSheet,
  8. Text,
  9. TextInput,
  10. TouchableOpacity,
  11. View,
  12. } from "react-native";
  13. import services from "../../services/api";
  14. const MAX_AMOUNT = 100000;
  15. export default function RechargeScreen() {
  16. const router = useRouter();
  17. const [money, setMoney] = useState("");
  18. const [loading, setLoading] = useState(false);
  19. const validateInput = (text: string) => {
  20. let value = text
  21. .replace(/[^\d.]/g, "")
  22. .replace(/^\./g, "")
  23. .replace(/\.{2,}/g, ".");
  24. value = value.replace(/^(-)*(\d+)\.(\d\d).*$/, "$1$2.$3");
  25. if (parseFloat(value) > MAX_AMOUNT) {
  26. Alert.alert("提示", `最大充值金额为${MAX_AMOUNT}元`);
  27. value = MAX_AMOUNT.toString();
  28. }
  29. setMoney(value);
  30. };
  31. const handleRecharge = async () => {
  32. const amount = parseFloat(money);
  33. if (!amount || amount <= 0) {
  34. Alert.alert("提示", "请输入有效的充值金额");
  35. return;
  36. }
  37. setLoading(true);
  38. setLoading(true);
  39. try {
  40. // Use ALIPAY_APP for native payment with correct API
  41. const res: any = await services.purse.rechargeSubmit(
  42. amount,
  43. "ALIPAY_APP",
  44. "CASH",
  45. );
  46. console.log("Recharge API Res:", res);
  47. // Handle response which might be direct string or object with payInfo
  48. const payInfo =
  49. typeof res?.data === "string"
  50. ? res?.data
  51. : res?.data?.payInfo || res?.data?.orderInfo || res?.data?.tradeNo;
  52. if (
  53. payInfo &&
  54. typeof payInfo === "string" &&
  55. (payInfo.startsWith("alipay_root_cert_sn") ||
  56. payInfo.includes("alipay_sdk"))
  57. ) {
  58. Alipay.setAlipayScheme("alipay2021005175632205");
  59. const result = await Alipay.pay(payInfo);
  60. console.log("Alipay Result:", result);
  61. if (result?.resultStatus === "9000") {
  62. Alert.alert("提示", "充值成功");
  63. router.replace("/wallet/recharge_record");
  64. } else {
  65. Alert.alert("提示", "支付未完成");
  66. }
  67. } else if (res?.data?.tradeNo && !payInfo) {
  68. Alert.alert("提示", "获取支付信息失败(仅获取到订单号)");
  69. } else {
  70. Alert.alert("失败", "生成充值订单失败 " + (res?.msg || ""));
  71. }
  72. } catch (error) {
  73. console.error("Recharge failed", error);
  74. Alert.alert("错误", "充值失败,请重试");
  75. } finally {
  76. setLoading(false);
  77. }
  78. };
  79. return (
  80. <View style={styles.container}>
  81. <Stack.Screen
  82. options={{
  83. title: "充值",
  84. headerStyle: { backgroundColor: "#fff" },
  85. headerShadowVisible: false,
  86. }}
  87. />
  88. <View style={styles.content}>
  89. <View style={styles.inputWrapper}>
  90. <Text style={styles.label}>充值金额:</Text>
  91. <TextInput
  92. style={styles.input}
  93. value={money}
  94. onChangeText={validateInput}
  95. placeholder="请输入金额"
  96. keyboardType="decimal-pad"
  97. />
  98. </View>
  99. <Text style={styles.tip}>充值金额可以用于购买手办,奖池</Text>
  100. <Text style={styles.tipSub}>
  101. 充值金额不可提现,
  102. <Text style={styles.highlight}>线下充值额外返10%</Text>
  103. </Text>
  104. <TouchableOpacity
  105. style={[styles.btn, loading && styles.disabledBtn]}
  106. onPress={handleRecharge}
  107. disabled={loading}
  108. >
  109. {loading ? (
  110. <ActivityIndicator color="#fff" />
  111. ) : (
  112. <Text style={styles.btnText}>充值</Text>
  113. )}
  114. </TouchableOpacity>
  115. <TouchableOpacity
  116. style={styles.recordLink}
  117. onPress={() => router.push("/wallet/recharge_record")}
  118. >
  119. <Text style={styles.recordLinkText}>
  120. 充值记录 {">"}
  121. {">"}
  122. </Text>
  123. </TouchableOpacity>
  124. </View>
  125. </View>
  126. );
  127. }
  128. const styles = StyleSheet.create({
  129. container: {
  130. flex: 1,
  131. backgroundColor: "#fff",
  132. },
  133. content: {
  134. paddingHorizontal: 24,
  135. paddingTop: 32,
  136. },
  137. inputWrapper: {
  138. flexDirection: "row",
  139. alignItems: "center",
  140. borderWidth: 1,
  141. borderColor: "#ddd",
  142. borderRadius: 8,
  143. paddingHorizontal: 16,
  144. height: 52, // 104rpx
  145. marginTop: 15,
  146. },
  147. label: {
  148. fontSize: 16, // font5
  149. color: "#333",
  150. marginRight: 10,
  151. },
  152. input: {
  153. flex: 1,
  154. fontSize: 16, // font5
  155. height: "100%",
  156. },
  157. tip: {
  158. textAlign: "center",
  159. color: "#666", // color-2
  160. fontSize: 14,
  161. marginTop: 36,
  162. },
  163. tipSub: {
  164. textAlign: "center",
  165. color: "#666", // color-2
  166. fontSize: 14,
  167. marginTop: 5,
  168. marginBottom: 20,
  169. },
  170. highlight: {
  171. color: "#07C160",
  172. textDecorationLine: "underline",
  173. },
  174. btn: {
  175. backgroundColor: "#0081ff", // bg-blue
  176. height: 44,
  177. borderRadius: 4,
  178. justifyContent: "center",
  179. alignItems: "center",
  180. width: "70%",
  181. alignSelf: "center",
  182. },
  183. disabledBtn: {
  184. opacity: 0.7,
  185. },
  186. btnText: {
  187. color: "#fff",
  188. fontSize: 14,
  189. fontWeight: "bold",
  190. },
  191. recordLink: {
  192. marginTop: 20,
  193. alignItems: "center",
  194. },
  195. recordLinkText: {
  196. color: "#8b3dff", // color-theme
  197. fontSize: 14,
  198. },
  199. });