/// <summary> /// 發送通知給特定user /// todo : 僅用於測試,待移除或權限控管 /// </summary> /// <param name="userId"></param> /// <param name="message"></param> /// <returns></returns> public async Task <IRpcMethodResult> sendMessage(string userId, string message) { var user = db.getUserByUserId(userId); await notifications.sendMessage(userId, user.ntfInfo.pns, message); return(Ok()); }
/// <summary> /// 發送通知給特定user /// todo : 僅用於測試,待移除或權限控管 /// </summary> /// <param name="userId"></param> /// <param name="message"></param> /// <returns></returns> public async Task <IRpcMethodResult> sendMessage(string userId, string message) { var uid = UserHelper.IdGen.Parse(userId); var userHelper = new UserHelper(); var user = await userHelper.GetById(uid); await notifications.sendMessage(user.userId, user.ntfInfo.pns, message); return(Ok()); }
/// <summary> /// 轉帳(付款) /// </summary> /// <param name="fromId"></param> /// <param name="toId"></param> /// <param name="currency"></param> /// <param name="amount"></param> /// <returns>receiptId</returns> public async Task <bool> doTransfer(string fromId, string toId, string receiptId, string currency, decimal amount, string message) { var ok = false; //get user var fromUser = getUserByUserId(fromId); var toUser = getUserByUserId(toId); //get user's balance var from_balance = getBalance(fromId).balances[currency]; var to_balance = getBalance(toId).balances[currency]; //simulate transcation if (!fromId.Equals(toId)) { if (amount > 0 && (from_balance >= amount)) { from_balance = from_balance - (fromUser.phoneno == "BANK" ? 0 : amount); to_balance = to_balance + amount; updateBalance(fromId, new Dictionary <string, decimal> { { currency, from_balance } }); updateBalance(toId, new Dictionary <string, decimal> { { currency, to_balance } }); ok = true; } } //generate sender receipt var param = new TxParams { sender = fromUser.userId, receiver = toUser.userId, currency = currency, amount = amount }; var sender_rec = new TxReceipt { receiptId = receiptId, executorId = fromUser.userId, ownerId = fromUser.userId, currency = currency, message = message, isParent = true, txType = TxType.TRANSFER, statusCode = ok ? 0 : -1, statusMsg = "", txParams = param, txResult = !ok ? null : new TxActResult { outflow = true, amount = amount, balance = from_balance } }; upsertReceipt(sender_rec); //simulate receipt notification Task.Run(async() => { Task.Delay(10).Wait(); //notify sender // if (fromUser.ntfInfo != null) // notifications.sendMessage(fromId, fromUser.ntfInfo.pns, $"transfer out({(ok ? "okay" : "failure")})", D.NTFTYPE.TXRECEIPT, new { list = new List<dynamic>() { sender_rec.ToApiResult() } }); //notify receiver if (ok && toUser.ntfInfo != null) { //generate receiver receipt var receiver_rec = sender_rec.Derivative(sender_rec.currency, toUser.userId, new TxActResult { outflow = false, amount = amount, balance = to_balance }); upsertReceipt(receiver_rec); await notifications.sendMessage(toId, toUser.ntfInfo.pns, "transfer in", D.NTFTYPE.TXRECEIPT, new { list = new List <dynamic>() { receiver_rec.ToApiResult() } }); } }).Start(); return(ok); }
/// <summary> /// 登入API service /// </summary> /// <param name="phoneno"></param> /// <param name="passcode"></param> /// <returns></returns> public async Task <IRpcMethodResult> login(string phoneno, string passcode, PNS pns, string pnsToken) { var smsHelper = new SmsPasscodeHelper(); var userHelper = new UserHelper(); try { // todo : remove this Console.WriteLine("萬用passcode未移除!"); if (passcode == "88888888") { } else { // 驗證sms passcode dynamic res = await smsHelper.IsSmsPasscodeMatched(phoneno, passcode); // 驗證失敗 if (!res.passed) { return(Bad(res.error)); } } // 取得user var user = await userHelper.GetByPhoneno(phoneno); var uid = (user != null) ? UserHelper.IdGen.Parse(user.userId) : userHelper.GenUid(); var tokenRnd = F.NewGuid(); var jwt = IssueToken(uid, tokenRnd); var jwtHash = tokenRnd.ToHash(); //todo : each user should have their own signature to verify //sign up if (user == null) { // create new user user = await userHelper.Create(uid, jwtHash, phoneno, phoneno); // reg pns token var nc = (RpcNotification)this.accessor.HttpContext.RequestServices.GetService(typeof(RpcNotification)); await nc.regPnsToken(pns, pnsToken, user); // db.addFriends(user.userId, new List<string> { }); // db.updateBalance(user.userId, new Dictionary<string, decimal>()); } //sign in else { // 通知前裝置必須登出 var ntfInfo = user.ntfInfo; if (ntfInfo != null && (ntfInfo.pns != pns || ntfInfo.pnsRegId != pnsToken)) { await notifications.sendMessage(user.userId, ntfInfo.pns, "someone logged into your account\\nyou've got to logout!(t1)", D.NTFTYPE.LOGOUT); // 避免rpc時間差可能造成regPnsToken在sendMessage之前 Task.Delay(3000).Wait(); } // 更新裝置pns token if (ntfInfo == null || ntfInfo.pns != pns || ntfInfo.pnsRegId != pnsToken) { var nc = (RpcNotification)this.accessor.HttpContext.RequestServices.GetService(typeof(RpcNotification)); await nc.regPnsToken(pns, pnsToken, user); } // update token hash user.jwtHash = jwtHash; await userHelper.Update(user); } return(Ok(new { token = jwt })); } catch (System.Exception e) { Console.WriteLine(e.ToString()); } return(ERROR_ACT_FAILED); }
/// <summary> /// 登入API service /// </summary> /// <param name="phoneno"></param> /// <param name="passcode"></param> /// <returns></returns> public async Task <IRpcMethodResult> login(string phoneno, string passcode, PNS pns, string pnsToken) { try { // 驗證sms passcode dynamic res = db.isSmsPasscodeMatched(phoneno, passcode); // todo : remove this Console.WriteLine("萬用passcode未移除!"); if (passcode == "88888888") { } else // 驗證失敗 if (!res.passed) { return(Bad(res.error)); } // 建立或取得user var user = db.getUserByPhone(phoneno); if (user != null) { // 通知前裝置必須登出 var ntfInfo = user.ntfInfo; if (ntfInfo != null && (ntfInfo.pns != pns || ntfInfo.pnsRegId != pnsToken)) { await Task.Run(async() => { await notifications.sendMessage(user.userId, ntfInfo.pns, "someone logged into your account\\nyou've got to logout!(t1)", D.NTFTYPE.LOGOUT); // 避免rpc時間差可能造成regPnsToken在sendMessage之前 Task.Delay(3000).Wait(); }); } // 更新裝置pns token if (ntfInfo == null || ntfInfo.pns != pns || ntfInfo.pnsRegId != pnsToken) { var nc = (RpcNotification)this.accessor.HttpContext.RequestServices.GetService(typeof(RpcNotification)); await nc.regPnsToken(pns, pnsToken, user); } } else { //todo : 暫時的假資料供測試 user = new User() { userId = "tempid-" + phoneno, //todo : 暫時以phoneno綁定id 便於識別 (日後移除) phoneno = phoneno, name = phoneno, // avatar = "https://ionicframework.com/dist/preview-app/www/assets/img/avatar-ts-woody.png", avatar = "", //empty for default currencies = new List <CurrencySettings> { new CurrencySettings { name = D.CNY, order = 0, isDefault = true, isVisible = false }, new CurrencySettings { name = D.USD, order = 1, isDefault = false, isVisible = false }, new CurrencySettings { name = D.BTC, order = 2, isDefault = false, isVisible = false }, new CurrencySettings { name = D.ETH, order = 3, isDefault = false, isVisible = false } } }; if (db.upsertUser(user)) { user = db.getUserByPhone(phoneno); } var nc = (RpcNotification)this.accessor.HttpContext.RequestServices.GetService(typeof(RpcNotification)); await nc.regPnsToken(pns, pnsToken, user); db.addFriends(user.userId, new List <string> { }); db.updateBalance(user.userId, new Dictionary <string, decimal>()); } // 發行token (JWT) var tokenRnd = F.NewGuid(); var claims = new Claim[] { new Claim(ClaimTypes.MobilePhone, phoneno), new Claim(ClaimTypes.Name, user.name), new Claim(ClaimTypes.Role, "User"), new Claim(D.CLAIM.USERID, user.userId), new Claim(D.CLAIM.TOKENRND, tokenRnd) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(R.JWT_SECRET)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( R.JWT_ISSUER, R.JWT_AUDIENCE, claims, null, //DateTime.UtcNow, //todo : 日後再決定是否每次token帶入時間加密 null, creds); var tokenStr = new JwtSecurityTokenHandler().WriteToken(token); // update token random user.tokenRnd = tokenRnd; db.upsertUser(user); return(Ok(new { token = tokenStr })); } catch (System.Exception e) { Console.WriteLine(e.ToString()); } return(ERROR_ACT_FAILED); }