XSNetwork.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. //
  2. // XSNetwork.m
  3. // XenonSDK
  4. //
  5. // Created by SAGESSE on 2019/5/28.
  6. // Copyright © 2019 SAGESSE. All rights reserved.
  7. //
  8. #import "XenonSDK.h"
  9. //#import "JSONModel.h"
  10. #import <JSONModel/JSONModel.h>
  11. #import "XSTracker.h"
  12. #import "XSUtils.h"
  13. #import <MBProgressHUD/MBProgressHUD.h>
  14. #import "KDIAPManager.h"
  15. #import <StoreKit/StoreKit.h>
  16. #if SDK_HAS_IAP_PAYMENT
  17. //#import "IAPAgnet.h"
  18. #endif
  19. @interface XSNetwork ()
  20. @end
  21. @implementation XSNetwork
  22. dispatch_semaphore_t sdk_net_semaphore;
  23. dispatch_queue_t sdk_net_waiting;
  24. NSInteger sdk_net_count;
  25. NSString* sdk_net_baseURL;
  26. MBProgressHUD* sdk_net_hud;
  27. + (NSString*)baseURL {
  28. return sdk_net_baseURL;
  29. }
  30. + (void)setBaseURL:(NSString *)baseURL {
  31. sdk_net_baseURL = baseURL;
  32. }
  33. + (NSError*)errorWithCode:(NSInteger)code message:(NSString*)message {
  34. if (message.length == 0) {
  35. message = @"";
  36. }
  37. return [NSError errorWithDomain:@"XSNetwork"
  38. code:code
  39. userInfo:@{NSLocalizedDescriptionKey:message}];
  40. }
  41. + (void)showHudLoading {
  42. dispatch_async(dispatch_get_main_queue(), ^{
  43. [sdk_net_hud removeFromSuperview];
  44. sdk_net_hud = [MBProgressHUD showHUDAddedTo:UIApplication.sharedApplication.keyWindow animated:YES];
  45. sdk_net_hud.label.text = nil;
  46. sdk_net_hud.removeFromSuperViewOnHide = YES;
  47. [sdk_net_hud layoutIfNeeded];
  48. [sdk_net_hud showAnimated:YES];
  49. });
  50. }
  51. + (void)showHudSuccess:(id)error {
  52. dispatch_async(dispatch_get_main_queue(), ^{
  53. [sdk_net_hud removeFromSuperview];
  54. sdk_net_hud = [MBProgressHUD showHUDAddedTo:UIApplication.sharedApplication.keyWindow animated:YES];
  55. sdk_net_hud.detailsLabel.text = error;
  56. sdk_net_hud.userInteractionEnabled = NO;
  57. sdk_net_hud.mode = MBProgressHUDModeText;
  58. //hud?.customView = UIImageView(image: UIImage(named: "success", in: nil, compatibleWith: nil))
  59. sdk_net_hud.removeFromSuperViewOnHide = YES;
  60. [sdk_net_hud hideAnimated:YES afterDelay:1.8];
  61. });
  62. }
  63. + (void)showHudFailure:(id)error {
  64. NSString* message = error;
  65. if ([error isKindOfClass:NSError.class]) {
  66. message = [error localizedDescription];
  67. }
  68. dispatch_async(dispatch_get_main_queue(), ^{
  69. [sdk_net_hud removeFromSuperview];
  70. sdk_net_hud = [MBProgressHUD showHUDAddedTo:UIApplication.sharedApplication.keyWindow animated:YES];
  71. sdk_net_hud.detailsLabel.text = message;
  72. sdk_net_hud.userInteractionEnabled = NO;
  73. sdk_net_hud.mode = MBProgressHUDModeText;
  74. //hud?.customView = UIImageView(image: UIImage(named: "error", in: nil, compatibleWith: nil))
  75. sdk_net_hud.removeFromSuperViewOnHide = YES;
  76. [sdk_net_hud layoutIfNeeded];
  77. [sdk_net_hud hideAnimated:YES afterDelay:2];
  78. });
  79. }
  80. + (void)hideHud {
  81. dispatch_async(dispatch_get_main_queue(), ^{
  82. [sdk_net_hud removeFromSuperview];
  83. });
  84. }
  85. + (void)showPrompt:(NSString*)user {
  86. // If no user information is provided, it is turned off by default.
  87. if (user.length == 0) {
  88. [self showHudSuccess:@"登录成功"];
  89. return;
  90. }
  91. // Hide the hud first to prevent errors.
  92. [self hideHud];
  93. UIWindow* window = UIApplication.sharedApplication.keyWindow;
  94. UIView* view = [[UIView alloc] initWithFrame:window.bounds];
  95. UIToolbar* prompt = [[UIToolbar alloc] init];
  96. UILabel* label = [[UILabel alloc] init];
  97. // A tag must be set and the view hierarchy needs to be reset when adding a hover window.
  98. view.tag = -1;
  99. view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  100. // Additional actions are allowed during the hud pop-up.
  101. view.userInteractionEnabled = NO;
  102. NSMutableAttributedString* value = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"欢迎 %@ 加入游戏", user]];
  103. [value addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:0 green:0.48 blue:1 alpha:1] range:NSMakeRange(2, user.length + 2)];
  104. label.attributedText = value;
  105. label.textAlignment = NSTextAlignmentCenter;
  106. label.numberOfLines = 0;
  107. label.font = [UIFont systemFontOfSize:14];
  108. CGSize size = [label sizeThatFits:CGSizeMake(window.frame.size.width - 80, 0)];
  109. CGFloat width = fmax(size.width + 32, 120);
  110. CGFloat height = fmax(size.height + 16, 32);
  111. CGFloat sp = 8;
  112. if (UIApplication.sharedApplication.statusBarFrame.size.height < 20) {
  113. sp += 16;
  114. }
  115. CGFloat top = UIApplication.sharedApplication.statusBarFrame.size.height + sp;
  116. if (@available(iOS 11.0, *)) {
  117. top = window.safeAreaInsets.top + sp;
  118. }
  119. prompt.layer.cornerRadius = 8;
  120. prompt.layer.masksToBounds = YES;
  121. prompt.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
  122. prompt.frame = CGRectMake((window.frame.size.width - width) / 2, top, width, height);
  123. label.frame = prompt.bounds;
  124. // [prompt addSubview:label];
  125. // [view addSubview:prompt];
  126. // [window addSubview:view];
  127. prompt.transform = CGAffineTransformMakeTranslation(0, -CGRectGetMaxY(prompt.frame));
  128. [UIView animateWithDuration:0.25 animations:^{
  129. prompt.transform = CGAffineTransformIdentity;
  130. } completion:^(BOOL t) {
  131. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  132. [UIView animateWithDuration:0.25 animations:^{
  133. prompt.transform = CGAffineTransformMakeTranslation(0, -CGRectGetMaxY(prompt.frame));
  134. } completion:^(BOOL x) {
  135. [view removeFromSuperview];
  136. }];
  137. });
  138. }];
  139. }
  140. + (NSData*)encryptWithParameters:(NSData*)data forKey:(NSString*)key {
  141. #if SDK_HAS_PROTECT_NETWORK_ENCRYPT
  142. if (data != nil) {
  143. NSString* dkey = sdk_md5(key);
  144. NSData* ddata = sdk_encrypt(data, [dkey substringFromIndex:dkey.length - 8]);
  145. return [ddata base64EncodedDataWithOptions:0];
  146. }
  147. #endif
  148. return data;
  149. }
  150. + (NSData*)decryptWithParameters:(NSData*)data forKey:(NSString*)key {
  151. #if SDK_HAS_PROTECT_NETWORK_ENCRYPT
  152. if (data != nil) {
  153. NSString* dkey = sdk_md5(key);
  154. NSData* ddata = [[NSData alloc] initWithBase64EncodedData:data options:0];
  155. return sdk_decrypt(ddata, [dkey substringFromIndex:dkey.length - 8]);
  156. }
  157. #endif
  158. return data;
  159. }
  160. + (NSDictionary*)bodyWithParameters:(id)parameters {
  161. // Generate request header parameters.
  162. NSString* timestamp = @((NSInteger)(NSDate.new.timeIntervalSince1970 * 1000)).stringValue;
  163. NSDictionary* header = @{
  164. @"appId": XenonSDK.sharedSDK.appId,
  165. @"channelId": XenonSDK.sharedSDK.channelId,
  166. @"adId": XenonSDK.sharedSDK.adId,
  167. @"adFlag": XenonSDK.sharedSDK.adFlag,
  168. @"sdkVersion": XenonSDK.sharedSDK.version,
  169. @"gameVersion": XenonSDK.sharedSDK.shortVersion,
  170. @"timestamp": timestamp,
  171. @"token": XenonSDK.sharedSDK.token ?: @"",
  172. @"osInfo": [NSString stringWithFormat:@"iOS%@", UIDevice.currentDevice.systemVersion],
  173. @"osType": @"2",
  174. @"idfa": XenonSDK.sharedSDK.advertisingIdentifier,
  175. @"idfv": XenonSDK.sharedSDK.advertisingVendor,
  176. @"model": XenonSDK.sharedSDK.model,
  177. @"wifi": /*Network.state.SSID ?? */@"Not Found",
  178. @"networkType": /*Network.state.isWifi ? "1" : */@"2", // 1 is wifi, 2 is other.
  179. @"tdid": XSCollector.shared.identifer ?: @"",
  180. @"trackData": @"",
  181. @"requestIp": @"",
  182. @"imei": @"",
  183. @"mac": @"",
  184. @"androidid": @"",
  185. @"verificationCode": sdk_signature(parameters, timestamp)
  186. };
  187. // Generate results, or add parameters dynamically.
  188. id result = @{
  189. @"head": header,
  190. @"body": parameters
  191. }.mutableCopy;
  192. // When the network needs encryption, 1-12 parameters are generated randomly in order to scramble the ciphertext length.
  193. #if SDK_HAS_PROTECT_NETWORK_ENCRYPT
  194. NSMutableArray* arr = [NSMutableArray new];
  195. for (int i = arc4random() % 12; i > 0; --i) {
  196. [arr addObject: sdk_md5([NSString stringWithFormat:@"%zu", (NSUInteger)arc4random()])];
  197. }
  198. [result setObject:arr forKey:@"other"];
  199. #endif
  200. return result;
  201. }
  202. + (void)busy {
  203. dispatch_async(dispatch_get_main_queue(), ^{
  204. sdk_net_count += 1;
  205. if (sdk_net_count == 1) {
  206. UIApplication.sharedApplication.networkActivityIndicatorVisible = YES;
  207. }
  208. });
  209. }
  210. + (void)idle {
  211. dispatch_async(dispatch_get_main_queue(), ^{
  212. if (sdk_net_count == 0) {
  213. return;
  214. }
  215. sdk_net_count = sdk_net_count - 1;
  216. if (sdk_net_count == 0) {
  217. UIApplication.sharedApplication.networkActivityIndicatorVisible = NO;
  218. }
  219. });
  220. }
  221. /// Suspended all network requests.
  222. + (void)suspend {
  223. if (sdk_net_waiting != nil) {
  224. return;
  225. }
  226. sdk_net_waiting = dispatch_queue_create("XSNetwork", 0);
  227. sdk_net_semaphore = dispatch_semaphore_create(0);
  228. dispatch_async(sdk_net_waiting, ^{
  229. // Lock the thread.
  230. dispatch_semaphore_wait(sdk_net_semaphore, DISPATCH_TIME_FOREVER);
  231. });
  232. }
  233. /// Resume all network requests.
  234. + (void)resume {
  235. if (sdk_net_waiting == nil) {
  236. return;
  237. }
  238. sdk_net_waiting = nil;
  239. dispatch_semaphore_signal(sdk_net_semaphore);
  240. sdk_net_semaphore = nil;
  241. }
  242. /// Send a request(maybe suspend).
  243. + (void)requestWithURL:(id)url parameters:(id)parameters model:(Class)model complete:(void(^)(id obj, NSError* error))complete {
  244. // Need wait from queue?
  245. if (sdk_net_waiting == nil) {
  246. [self requestWithoutSuspend:url parameters:parameters model:model complete:complete];
  247. return;
  248. }
  249. // Enter waiting status.
  250. dispatch_async(sdk_net_waiting, ^{
  251. dispatch_async(dispatch_get_main_queue(), ^{
  252. [self requestWithoutSuspend:url parameters:parameters model:model complete:complete];
  253. });
  254. });
  255. }
  256. /// Send a request.
  257. + (void)requestWithoutSuspend:(id)url parameters:(id)parameters model:(Class)model complete:(void(^)(id obj, NSError* error))complete {
  258. // Generate encryption options.
  259. #if SDK_HAS_PROTECT_NETWORK_ENCRYPT
  260. NSUInteger options = arc4random() | 1;
  261. #else
  262. NSUInteger options = arc4random() & ~1;
  263. #endif
  264. // Convert to URL
  265. NSString* path = [NSString stringWithFormat:@"/data/%@-%@-%@-%zd", XenonSDK.sharedSDK.appId, XenonSDK.sharedSDK.adId, url, options];
  266. NSURL* req = [NSURL URLWithString:path relativeToURL:[NSURL URLWithString: self.baseURL]];
  267. if (req == nil) {
  268. complete(nil, nil);
  269. return;
  270. }
  271. // Generate request & parameters.
  272. NSDictionary* body = [self bodyWithParameters:parameters];
  273. NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:req cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60];
  274. NSError* serror = nil;
  275. NSData* sdata = [NSJSONSerialization dataWithJSONObject:body options:0 error:&serror];
  276. if (sdata == nil || serror != nil) {
  277. complete(nil, serror);
  278. }
  279. request.HTTPBody = [self encryptWithParameters:sdata forKey:path];
  280. request.HTTPMethod = @"POST";
  281. [request addValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
  282. //#if DEBUG
  283. // NSLog(@"%@ << %@", path, [[NSString alloc] initWithData:sdata encoding:NSUTF8StringEncoding]);
  284. //#endif
  285. // Network start request.
  286. [self busy];
  287. // Send a request to server.
  288. NSURLSessionDataTask* task = [NSURLSession.sharedSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  289. // Must be calleback in the main thread.
  290. dispatch_async(dispatch_get_main_queue(), ^{
  291. // Network request completed.
  292. [self idle];
  293. // decode data;
  294. NSData* ddata = [self decryptWithParameters:data forKey:path];
  295. NSError* derror = nil;
  296. NSDictionary* djson = nil;
  297. if (ddata) {
  298. djson = [NSJSONSerialization JSONObjectWithData:ddata options:0 error:&derror];
  299. }
  300. // #if DEBUG
  301. // NSLog(@"%@ >> %@", path, error.description ?: [[NSString alloc] initWithData:ddata encoding:NSUTF8StringEncoding]);
  302. // #endif
  303. // If data is nil, the request is failure.
  304. if (data == nil || error != nil || derror != nil) {
  305. // Request failure for system error.
  306. complete(nil, error ?: derror);
  307. return;
  308. }
  309. id header = djson[@"head"];
  310. if ([header[@"responseCode"] integerValue] != 0) {
  311. // Request failure for system error.
  312. NSInteger code = [header[@"responseCode"] integerValue];
  313. NSString* message = header[@"responseMsg"];
  314. complete(nil, [self errorWithCode:code message:message]);
  315. return;
  316. }
  317. // In request success case.
  318. id dbody = djson[@"body"];
  319. id obj = [[model alloc] initWithDictionary:dbody error:&derror];
  320. if (obj == nil && derror == nil) {
  321. obj = dbody;
  322. }
  323. complete(obj , derror);
  324. });
  325. }];
  326. // Start.
  327. [task resume];
  328. }
  329. /// Request systme configure.
  330. + (void)configureWithIdentifier:(NSString*)identifier flags:(NSString*)flags complete:(void(^)(id object, NSError* error))complete {
  331. // Prepare request parameters.
  332. id url = @"0";//"/api/game/v1/active"
  333. id ps = @{
  334. @"adId": identifier,
  335. @"adFlag": flags
  336. };
  337. // Suspend requests for other.
  338. [self suspend];
  339. // Send a request to server.
  340. [self requestWithoutSuspend:url parameters:ps model:XSConfiguration.self complete:^(id obj, NSError *error) {
  341. // Resume requests for other.
  342. [self resume];
  343. // Next.
  344. complete(obj, error);
  345. }];
  346. }
  347. /// Send SMS security code.
  348. + (void)sendSMSWithPhone:(NSString*)phone type:(NSInteger)type complete:(void(^)(id object, NSError* error))complete {
  349. // Prepare request parameters.
  350. id url = @"7";//"/api/sms/v1/send"
  351. id ps = @{
  352. @"phone": phone,
  353. @"sendType": @(type).stringValue // 1 bind, 2 forget, 3 login
  354. };
  355. // Send a request to server.
  356. [self requestWithURL:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  357. complete(nil, error);
  358. }];
  359. }
  360. /// Auth login.
  361. + (void)authWithUser:(NSString*)uid complete:(void(^)(id object, NSError* error))complete {
  362. // Prepare request parameters.
  363. id url = @"1";//"/api/user/v1/checkToken"
  364. id ps = @{
  365. @"uid": uid
  366. };
  367. // Send a request to server.
  368. [self requestWithURL:url parameters:ps model:XSUser.self complete:complete];
  369. }
  370. /// Check user with phone
  371. + (void)checkWithPhone:(NSString*)phone complete:(void(^)(id object, NSError* error))complete {
  372. // Prepare request parameters.
  373. id url = @"5";//"/api/user/v1/checkUserPhone"
  374. id ps = @{
  375. @"phone": phone
  376. };
  377. // Send a request to server.
  378. [self requestWithURL:url parameters:ps model:nil complete:complete];
  379. }
  380. /// Bind user with phone.
  381. + (void)bindWithPhone:(NSString*)phone code:(NSString*)code username:(NSString*)username complete:(void(^)(id object, NSError* error))complete {
  382. // Prepare request parameters.
  383. id url = @"6";//"/api/user/v1/bindPhone"
  384. id ps = @{
  385. @"userName": username,
  386. @"phone": phone,
  387. @"code": code
  388. };
  389. // Send a request to server.
  390. [self requestWithURL:url parameters:ps model:nil complete:complete];
  391. }
  392. /// Reset password.
  393. + (void)resetWithPhone:(NSString*)phone code:(NSString*)code password:(NSString*)password complete:(void(^)(id object, NSError* error))complete {
  394. // Prepare request parameters.
  395. id url = @"4";//"/api/user/v1/forget"
  396. id ps = @{
  397. @"phone": phone,
  398. @"pwd": [sdk_md5(password) uppercaseString],
  399. @"code": code
  400. };
  401. // Send a request to server.
  402. [self requestWithURL:url parameters:ps model:nil complete:complete];
  403. }
  404. /// Fast login a user.
  405. + (void)loginWithComplete:(void(^)(id object, NSError* error))complete {
  406. // Prepare request parameters.
  407. id url = @"2";//"/api/user/v1/register"
  408. id ps = @{
  409. @"userName": @"",
  410. @"pwd": @"",
  411. @"code":@"",
  412. @"loginType": @"0" // falst login
  413. };
  414. // Send a request to server.
  415. [self requestWithURL:url parameters:ps model:XSUser.self complete:^(XSUser* user, NSError *error) {
  416. if (user != nil) {
  417. if (user.isRegister) {
  418. [XSCollector.shared registerWithAccount:user.uid name:user.userName];
  419. } else {
  420. [XSCollector.shared loginWithAccount:user.uid name:user.userName];
  421. }
  422. }
  423. complete(user, error);
  424. }];
  425. }
  426. /// Phone & Security Code login a user.
  427. + (void)loginWithPhone:(NSString*)phone code:(NSString*)code complete:(void(^)(id object, NSError* error))complete {
  428. // Prepare request parameters.
  429. id url = @"3";//"/api/user/v1/login"
  430. id ps = @{
  431. @"userName": phone,
  432. @"pwd": code,
  433. @"loginType": @"3" // Phone & Security Code login
  434. };
  435. // Send a request to server.
  436. [self requestWithURL:url parameters:ps model:XSUser.self complete:^(XSUser* user, NSError *error) {
  437. user.account = phone;
  438. user.pwd = nil;
  439. if (user != nil) {
  440. if (user.isRegister) {
  441. [XSCollector.shared registerWithAccount:user.uid name:user.userName];
  442. } else {
  443. [XSCollector.shared loginWithAccount:user.uid name:user.userName];
  444. }
  445. }
  446. complete(user, error);
  447. }];
  448. }
  449. /// Account & Password login a user.
  450. + (void)loginWithAccount:(NSString*)account password:(NSString*)password complete:(void(^)(id object, NSError* error))complete {
  451. // Prepare request parameters.
  452. id url = @"3";//"/api/user/v1/login"
  453. id ps = @{
  454. @"userName": account,
  455. @"pwd": [sdk_md5(password) uppercaseString],
  456. @"loginType": @"1" // account login
  457. };
  458. // Send a request to server.
  459. [self requestWithURL:url parameters:ps model:XSUser.self complete:^(XSUser* user, NSError *error) {
  460. user.account = account;
  461. user.pwd = password;
  462. if (user != nil) {
  463. [XSCollector.shared loginWithAccount:user.uid name:user.userName];
  464. }
  465. complete(user, error);
  466. }];
  467. }
  468. /// Account & Password login a user.
  469. + (void)registerWithAccount:(NSString*)account password:(NSString*)password complete:(void(^)(id object, NSError* error))complete {
  470. // Prepare request parameters.
  471. id url = @"2";//"/api/user/v1/register"
  472. id ps = @{
  473. @"userName": account,
  474. @"pwd": [sdk_md5(password) uppercaseString],
  475. @"loginType": @"1" // account login
  476. };
  477. // Send a request to server.
  478. [self requestWithURL:url parameters:ps model:XSUser.self complete:^(XSUser* user, NSError *error) {
  479. user.account = account;
  480. user.pwd = password;
  481. user.isRegister = YES;
  482. if (user != nil) {
  483. [XSCollector.shared registerWithAccount:user.uid name:user.userName];
  484. }
  485. complete(user, error);
  486. }];
  487. }
  488. // MARK - 广告记录
  489. +(void)adRecord:(NSString *)action
  490. spaceId:(NSString *)spaceId
  491. agentName:(NSString *)agentName
  492. medium:(NSString *)medium
  493. adUnitId:(NSString *)adUnitId
  494. type:(NSString *)type
  495. unitAdId:(NSString *)unitAdId
  496. errorMsg:(NSString *)errorMeg{
  497. NSString *uid = [XenonSDK sharedSDK].user.uid;
  498. //广告组ID,在plist文件里配置.
  499. //NSBundle *mainBundle = [NSBundle mainBundle];
  500. //NSString *unitAdId1 = mainBundle.infoDictionary[@"ironsource-app-unitAdId"];
  501. id url = @"e"; //"/api/game/v1/adAction"
  502. id ps = @{
  503. @"action": action,
  504. @"spaceId": spaceId,
  505. @"uid" : uid? uid:@"",
  506. @"agentName" : agentName,
  507. @"medium" : medium,
  508. @"adUnitId" : adUnitId? adUnitId:@"",
  509. @"type" : type,
  510. @"unitAdId" : unitAdId,
  511. @"errmsg" : errorMeg
  512. };
  513. // Send a request to server.
  514. [self requestWithoutSuspend:url parameters:ps model:XSUser.self complete:^(id obj, NSError *error) {
  515. //complete(obj, error);
  516. }];
  517. }
  518. // MARK - Utils
  519. /// Auto trigger;
  520. + (void)trigger:(NSInteger)count {
  521. #if SDK_HAS_PROTECT_NETWORK_PRETEND
  522. // If tracking is not enabled, turn it off.
  523. if (XSTracker.shared == nil) {
  524. return;
  525. }
  526. // Prepare request parameters.
  527. NSString* url = @"*";
  528. NSMutableDictionary* ps = [NSMutableDictionary new];
  529. for (int i = arc4random() % 12 + 1; i > 0; --i) {
  530. id key = [sdk_md5(@(arc4random()).stringValue) substringToIndex:((arc4random() % 14) + 1)];
  531. id value = [[key dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
  532. ps[key] = value;
  533. }
  534. // Send a request to server.
  535. [self requestWithURL:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  536. if (error == nil || count > 0) {
  537. return;
  538. }
  539. NSInteger ft = arc4random() % 10000;
  540. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * ft), dispatch_get_main_queue(), ^{
  541. [self trigger:count - 1];
  542. });
  543. }];
  544. #endif
  545. }
  546. + (void)log:(NSInteger)code session:(NSString*)session extra:(NSString*)extra {
  547. // Prepare request parameters.
  548. id url = @"9";//"/api/ardent/v1/add"
  549. id ps = @{
  550. @"digitId": [NSString stringWithFormat:@"0x%06zx", code],
  551. @"sessionId": session,
  552. @"iosStr": extra
  553. };
  554. // Send a request to server.
  555. [self requestWithoutSuspend:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  556. // Nothing.
  557. }];
  558. }
  559. + (void)reportWithUser:(NSString*)uid Name:(NSString*)name level:(NSInteger)level server:(NSString*)server {
  560. // Prepare request parameters.
  561. id url = @"a";//"/api/game/v1/sendServer"
  562. id ps = @{
  563. @"uid": uid ?: @"",
  564. @"gameServerId": server,
  565. @"roleLev": @(level).stringValue,
  566. @"roleName": name
  567. };
  568. // Send a request to server.
  569. [self requestWithURL:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  570. }];
  571. }
  572. /// Get pay route.
  573. + (void)routeWithComplete:(void(^)(id object, NSError* error))complete {
  574. // Prepare request parameters.
  575. id url = @"8";//"/api/p/v1/pCheck"
  576. id ps = @{
  577. @"uid": XenonSDK.sharedSDK.user.uid ?: @""
  578. };
  579. // Send a request to server.
  580. [self requestWithURL:url parameters:ps model:nil complete:complete];
  581. }
  582. #if SDK_HAS_IAP_PAYMENT
  583. /// Pay a product.
  584. + (void)payWithParameters:(id)parameters complete:(void(^)(id object, NSError* error))complete {
  585. // Prepare request parameters.
  586. id url = @"b";//"/api/p/v1/apple/create"
  587. id ps = parameters;
  588. // Check if in-app purchases are available.
  589. if (!SKPaymentQueue.canMakePayments) {
  590. complete(nil, [XSNetwork errorWithCode:-2 message:@"该设备不支持内购"]);
  591. return;
  592. }
  593. // Send a request to server.
  594. [self requestWithURL:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  595. if (error != nil) {
  596. complete(nil, error);
  597. return;
  598. }
  599. // Get prarmeter
  600. NSString* orderId = [obj valueForKeyPath:@"orderId"];
  601. NSString* productId = [obj valueForKeyPath:@"product.productId"];
  602. if (orderId.length == 0 || productId.length == 0) {
  603. complete(nil, [XSNetwork errorWithCode:-2 message:@"服务器异常"]);
  604. return;
  605. }
  606. [[KDIAPManager shareIAPManager] startIAPWithOrderId:orderId productID:productId completeHandle:^(IAPResultType type, NSData *data) {
  607. //complete(nil, [XSNetwork errorWithCode:-2 message:@"购买成功!"]);s
  608. if (type == IAPResultSuccess) {
  609. [XSNetwork showHudSuccess:@"购买成功!"];
  610. //complete(ps,nil);
  611. complete(ps, [XSNetwork errorWithCode:-2 message:@"购买成功!"]);
  612. }else if (type == IAPResultVerFailed){
  613. [XSNetwork showHudSuccess:@"订单校验失败!"];
  614. complete(nil, [XSNetwork errorWithCode:-2 message:@"订单校验失败!"]);
  615. }else{
  616. [XSNetwork showHudFailure:@"购买失败!"];
  617. complete(nil, [XSNetwork errorWithCode:-2 message:@"购买失败!"]);
  618. }
  619. }];
  620. }];
  621. }
  622. /// Pay a product with receipt. 客户端发送苹果的票据到服务器端校验.
  623. + (void)payWithReceipt:(id)receipt orderId:(id)orderId complete:(void(^)(id object, NSError* error))complete {
  624. // Prepare request parameters.
  625. id url = @"c";//"/api/p/v1/apple/p"
  626. id ps = @{
  627. @"data": receipt,
  628. @"orderId": orderId,
  629. @"uid": XenonSDK.sharedSDK.user.uid ?: @""
  630. };
  631. // Send a request to server.
  632. [self requestWithURL:url parameters:ps model:nil complete:complete];
  633. }
  634. #endif
  635. @end