diff --git a/Storage/Storage-Unity/Storage-Unity.csproj b/Storage/Storage-Unity/Storage-Unity.csproj
index 5eca288..1151018 100644
--- a/Storage/Storage-Unity/Storage-Unity.csproj
+++ b/Storage/Storage-Unity/Storage-Unity.csproj
@@ -117,6 +117,12 @@
Internal\Query\LCCompositionalCondition.cs
+
+ LCCaptchaClient.cs
+
+
+ LCSMSClient.cs
+
diff --git a/Storage/Storage.Test/CaptchaTest.cs b/Storage/Storage.Test/CaptchaTest.cs
new file mode 100644
index 0000000..773ed3a
--- /dev/null
+++ b/Storage/Storage.Test/CaptchaTest.cs
@@ -0,0 +1,36 @@
+using NUnit.Framework;
+using System.Threading.Tasks;
+using LeanCloud;
+using LeanCloud.Storage;
+
+using static NUnit.Framework.TestContext;
+
+namespace Storage.Test {
+ public class CaptchaTest {
+ [SetUp]
+ public void SetUp() {
+ LCLogger.LogDelegate += Utils.Print;
+ LCApplication.Initialize(Utils.AppId, Utils.AppKey, Utils.AppServer);
+ }
+
+ [TearDown]
+ public void TearDown() {
+ LCLogger.LogDelegate -= Utils.Print;
+ }
+
+ //[Test]
+ public async Task Request() {
+ LCCapture captcha = await LCCaptchaClient.RequestCaptcha();
+ WriteLine($"url: {captcha.Url}");
+ WriteLine($"token: {captcha.Token}");
+ Assert.NotNull(captcha);
+ Assert.NotNull(captcha.Url);
+ Assert.NotNull(captcha.Token);
+ }
+
+ //[Test]
+ public async Task Verify() {
+ await LCCaptchaClient.VerifyCaptcha("on2r", "1TUDkEMu");
+ }
+ }
+}
diff --git a/Storage/Storage.Test/SMSTest.cs b/Storage/Storage.Test/SMSTest.cs
new file mode 100644
index 0000000..cb76dad
--- /dev/null
+++ b/Storage/Storage.Test/SMSTest.cs
@@ -0,0 +1,40 @@
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using LeanCloud;
+using LeanCloud.Storage;
+
+namespace Storage.Test {
+ public class SMSTest {
+ [SetUp]
+ public void SetUp() {
+ LCLogger.LogDelegate += Utils.Print;
+ LCApplication.Initialize(Utils.AppId, Utils.AppKey, Utils.AppServer);
+ }
+
+ [TearDown]
+ public void TearDown() {
+ LCLogger.LogDelegate -= Utils.Print;
+ }
+
+ //[Test]
+ public async Task RequestSMS() {
+ await LCSMSClient.RequestSMSCode("15101006007",
+ template: "test_template",
+ signature: "flutter-test",
+ variables: new Dictionary {
+ { "k1", "v1" }
+ });
+ }
+
+ //[Test]
+ public async Task RequestVoice() {
+ await LCSMSClient.RequestVoiceCode("+8615101006007");
+ }
+
+ //[Test]
+ public async Task Verify() {
+ await LCSMSClient.VerifyMobilePhone("15101006007", "");
+ }
+ }
+}
diff --git a/Storage/Storage.Test/Utils.cs b/Storage/Storage.Test/Utils.cs
index e7e6df2..3876751 100644
--- a/Storage/Storage.Test/Utils.cs
+++ b/Storage/Storage.Test/Utils.cs
@@ -3,6 +3,10 @@ using LeanCloud;
namespace Storage.Test {
public static class Utils {
+ internal const string AppId = "ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz";
+ internal const string AppKey = "NUKmuRbdAhg1vrb2wexYo1jo";
+ internal const string AppServer = "https://ikggdre2.lc-cn-n1-shared.com";
+
internal static void Print(LCLogLevel level, string info) {
switch (level) {
case LCLogLevel.Debug:
diff --git a/Storage/Storage/LCCaptchaClient.cs b/Storage/Storage/LCCaptchaClient.cs
new file mode 100644
index 0000000..968c730
--- /dev/null
+++ b/Storage/Storage/LCCaptchaClient.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LeanCloud.Storage {
+ ///
+ /// 验证码
+ ///
+ public class LCCapture {
+ public string Url {
+ get; set;
+ }
+
+ public string Token {
+ get; set;
+ }
+ }
+
+ ///
+ /// 验证码工具类
+ ///
+ public static class LCCaptchaClient {
+ ///
+ /// 请求验证码
+ ///
+ /// 验证码图片宽度
+ /// 验证码图片高度
+ ///
+ public static async Task RequestCaptcha(int width = 82,
+ int height = 39) {
+ string path = "requestCaptcha";
+ Dictionary queryParams = new Dictionary {
+ { "width", width },
+ { "height", height }
+ };
+ Dictionary response = await LCApplication.HttpClient.Get>(path, queryParams: queryParams);
+ return new LCCapture {
+ Url = response["captcha_url"] as string,
+ Token = response["captcha_token"] as string
+ };
+ }
+
+ ///
+ /// 验证
+ ///
+ ///
+ ///
+ ///
+ public static async Task VerifyCaptcha(string code,
+ string token) {
+ if (string.IsNullOrEmpty(code)) {
+ throw new ArgumentNullException(nameof(code));
+ }
+ if (string.IsNullOrEmpty(token)) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ string path = "verifyCaptcha";
+ Dictionary data = new Dictionary {
+ { "captcha_code", code },
+ { "captcha_token", token }
+ };
+ await LCApplication.HttpClient.Post>(path, data: data);
+ }
+ }
+}
diff --git a/Storage/Storage/LCSMSClient.cs b/Storage/Storage/LCSMSClient.cs
new file mode 100644
index 0000000..8688995
--- /dev/null
+++ b/Storage/Storage/LCSMSClient.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LeanCloud.Storage {
+ ///
+ /// 短信工具类
+ ///
+ public static class LCSMSClient {
+ ///
+ /// 请求短信验证码
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task RequestSMSCode(string mobile,
+ string template = null,
+ string signature = null,
+ string captchaToken = null,
+ Dictionary variables = null) {
+ if (string.IsNullOrEmpty(mobile)) {
+ throw new ArgumentNullException(nameof(mobile));
+ }
+
+ string path = "requestSmsCode";
+ Dictionary data = new Dictionary {
+ { "mobilePhoneNumber", mobile }
+ };
+ if (!string.IsNullOrEmpty(template)) {
+ data["template"] = template;
+ }
+ if (!string.IsNullOrEmpty(signature)) {
+ data["sign"] = signature;
+ }
+ if (!string.IsNullOrEmpty(captchaToken)) {
+ data["validate_token"] = captchaToken;
+ }
+ if (variables != null) {
+ foreach (KeyValuePair kv in variables) {
+ data[kv.Key] = kv.Value;
+ }
+ }
+ await LCApplication.HttpClient.Post>(path, data: data);
+ }
+
+ ///
+ /// 请求语音验证码
+ ///
+ ///
+ ///
+ public static async Task RequestVoiceCode(string mobile) {
+ string path = "requestSmsCode";
+ Dictionary data = new Dictionary {
+ { "mobilePhoneNumber", mobile },
+ { "smsType", "voice" }
+ };
+ await LCApplication.HttpClient.Post>(path, data: data);
+ }
+
+ ///
+ /// 验证手机号
+ ///
+ ///
+ ///
+ ///
+ public static async Task VerifyMobilePhone(string mobile, string code) {
+ string path = $"verifySmsCode/{code}";
+ Dictionary data = new Dictionary {
+ { "mobilePhoneNumber", mobile }
+ };
+ await LCApplication.HttpClient.Post>(path, data: data);
+ }
+ }
+}