XSNetwork.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  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.h"
  14. #import <MBProgressHUD/MBProgressHUD.h>
  15. #if SDK_HAS_IAP_PAYMENT
  16. #import "IAPAgnet.h"
  17. #import "KDIAPManager.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. @"loginType": @"0" // falst login
  412. };
  413. // Send a request to server.
  414. [self requestWithURL:url parameters:ps model:XSUser.self complete:^(XSUser* user, NSError *error) {
  415. if (user != nil) {
  416. if (user.isRegister) {
  417. [XSCollector.shared registerWithAccount:user.uid name:user.userName];
  418. } else {
  419. [XSCollector.shared loginWithAccount:user.uid name:user.userName];
  420. }
  421. }
  422. complete(user, error);
  423. }];
  424. }
  425. /// Phone & Security Code login a user.
  426. + (void)loginWithPhone:(NSString*)phone code:(NSString*)code complete:(void(^)(id object, NSError* error))complete {
  427. // Prepare request parameters.
  428. id url = @"3";//"/api/user/v1/login"
  429. id ps = @{
  430. @"userName": phone,
  431. @"pwd": code,
  432. @"loginType": @"3" // Phone & Security Code login
  433. };
  434. // Send a request to server.
  435. [self requestWithURL:url parameters:ps model:XSUser.self complete:^(XSUser* user, NSError *error) {
  436. user.account = phone;
  437. user.pwd = nil;
  438. if (user != nil) {
  439. if (user.isRegister) {
  440. [XSCollector.shared registerWithAccount:user.uid name:user.userName];
  441. } else {
  442. [XSCollector.shared loginWithAccount:user.uid name:user.userName];
  443. }
  444. }
  445. complete(user, error);
  446. }];
  447. }
  448. /// Account & Password login a user.
  449. + (void)loginWithAccount:(NSString*)account password:(NSString*)password complete:(void(^)(id object, NSError* error))complete {
  450. // Prepare request parameters.
  451. id url = @"3";//"/api/user/v1/login"
  452. id ps = @{
  453. @"userName": account,
  454. @"pwd": [sdk_md5(password) uppercaseString],
  455. @"loginType": @"1" // account login
  456. };
  457. // Send a request to server.
  458. [self requestWithURL:url parameters:ps model:XSUser.self complete:^(XSUser* user, NSError *error) {
  459. user.account = account;
  460. user.pwd = password;
  461. if (user != nil) {
  462. [XSCollector.shared loginWithAccount:user.uid name:user.userName];
  463. }
  464. complete(user, error);
  465. }];
  466. }
  467. /// Account & Password login a user.
  468. + (void)registerWithAccount:(NSString*)account password:(NSString*)password complete:(void(^)(id object, NSError* error))complete {
  469. // Prepare request parameters.
  470. id url = @"2";//"/api/user/v1/register"
  471. id ps = @{
  472. @"userName": account,
  473. @"pwd": [sdk_md5(password) uppercaseString],
  474. @"loginType": @"1" // account login
  475. };
  476. // Send a request to server.
  477. [self requestWithURL:url parameters:ps model:XSUser.self complete:^(XSUser* user, NSError *error) {
  478. user.account = account;
  479. user.pwd = password;
  480. user.isRegister = YES;
  481. if (user != nil) {
  482. [XSCollector.shared registerWithAccount:user.uid name:user.userName];
  483. }
  484. complete(user, error);
  485. }];
  486. }
  487. // MARK - 广告记录
  488. +(void)adRecord:(NSString *)action
  489. spaceId:(NSString *)spaceId
  490. agentName:(NSString *)agentName
  491. medium:(NSString *)medium
  492. adUnitId:(NSString *)adUnitId
  493. type:(NSString *)type
  494. unitAdId:(NSString *)unitAdId
  495. errorMsg:(NSString *)errorMeg{
  496. NSString *uid = [XenonSDK sharedSDK].user.uid;
  497. //广告组ID,在plist文件里配置.
  498. //NSBundle *mainBundle = [NSBundle mainBundle];
  499. //NSString *unitAdId1 = mainBundle.infoDictionary[@"ironsource-app-unitAdId"];
  500. id url = @"e"; //"/api/game/v1/adAction"
  501. id ps = @{
  502. @"action": action,
  503. @"spaceId": spaceId,
  504. @"uid" : uid? uid:@"",
  505. @"agentName" : agentName,
  506. @"medium" : medium,
  507. @"adUnitId" : adUnitId? adUnitId:@"",
  508. @"type" : type,
  509. @"unitAdId" : unitAdId,
  510. @"errmsg" : errorMeg
  511. };
  512. // Send a request to server.
  513. [self requestWithoutSuspend:url parameters:ps model:XSUser.self complete:^(id obj, NSError *error) {
  514. //complete(obj, error);
  515. }];
  516. }
  517. // MARK - Utils
  518. /// Auto trigger;
  519. + (void)trigger:(NSInteger)count {
  520. #if SDK_HAS_PROTECT_NETWORK_PRETEND
  521. // If tracking is not enabled, turn it off.
  522. if (XSTracker.shared == nil) {
  523. return;
  524. }
  525. // Prepare request parameters.
  526. NSString* url = @"*";
  527. NSMutableDictionary* ps = [NSMutableDictionary new];
  528. for (int i = arc4random() % 12 + 1; i > 0; --i) {
  529. id key = [sdk_md5(@(arc4random()).stringValue) substringToIndex:((arc4random() % 14) + 1)];
  530. id value = [[key dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
  531. ps[key] = value;
  532. }
  533. // Send a request to server.
  534. [self requestWithURL:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  535. if (error == nil || count > 0) {
  536. return;
  537. }
  538. NSInteger ft = arc4random() % 10000;
  539. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * ft), dispatch_get_main_queue(), ^{
  540. [self trigger:count - 1];
  541. });
  542. }];
  543. #endif
  544. }
  545. + (void)log:(NSInteger)code session:(NSString*)session extra:(NSString*)extra {
  546. // Prepare request parameters.
  547. id url = @"9";//"/api/ardent/v1/add"
  548. id ps = @{
  549. @"digitId": [NSString stringWithFormat:@"0x%06zx", code],
  550. @"sessionId": session,
  551. @"iosStr": extra
  552. };
  553. // Send a request to server.
  554. [self requestWithoutSuspend:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  555. // Nothing.
  556. }];
  557. }
  558. + (void)reportWithUser:(NSString*)uid Name:(NSString*)name level:(NSInteger)level server:(NSString*)server {
  559. // Prepare request parameters.
  560. id url = @"a";//"/api/game/v1/sendServer"
  561. id ps = @{
  562. @"uid": uid ?: @"",
  563. @"gameServerId": server,
  564. @"roleLev": @(level).stringValue,
  565. @"roleName": name
  566. };
  567. // Send a request to server.
  568. [self requestWithURL:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  569. }];
  570. }
  571. /// Get pay route.
  572. + (void)routeWithComplete:(void(^)(id object, NSError* error))complete {
  573. // Prepare request parameters.
  574. id url = @"8";//"/api/p/v1/pCheck"
  575. id ps = @{
  576. @"uid": XenonSDK.sharedSDK.user.uid ?: @""
  577. };
  578. // Send a request to server.
  579. [self requestWithURL:url parameters:ps model:nil complete:complete];
  580. }
  581. #if SDK_HAS_IAP_PAYMENT
  582. /// Pay a product.
  583. + (void)payWithParameters:(id)parameters complete:(void(^)(id object, NSError* error))complete {
  584. // Prepare request parameters.
  585. id url = @"b";//"/api/p/v1/apple/create"
  586. id ps = parameters;
  587. // Check if in-app purchases are available.
  588. if (!SKPaymentQueue.canMakePayments) {
  589. complete(nil, [XSNetwork errorWithCode:-2 message:@"该设备不支持内购"]);
  590. return;
  591. }
  592. // Send a request to server.
  593. [self requestWithURL:url parameters:ps model:nil complete:^(id obj, NSError *error) {
  594. if (error != nil) {
  595. complete(nil, error);
  596. return;
  597. }
  598. // Get prarmeter
  599. NSString* orderId = [obj valueForKeyPath:@"orderId"];
  600. NSString* productId = [obj valueForKeyPath:@"product.productId"];
  601. if (orderId.length == 0 || productId.length == 0) {
  602. complete(nil, [XSNetwork errorWithCode:-2 message:@"服务器异常"]);
  603. return;
  604. }
  605. [[KDIAPManager shareIAPManager] startIAPWithOrderId:orderId productID:productId completeHandle:^(IAPResultType type, NSData *data) {
  606. //complete(nil, [XSNetwork errorWithCode:-2 message:@"购买成功!"]);
  607. if (type == IAPResultSuccess) {
  608. [XSNetwork showHudSuccess:@"购买成功!"];
  609. complete(ps,nil);
  610. }else if (type == IAPResultVerFailed){
  611. [XSNetwork showHudSuccess:@"订单校验失败!"];
  612. complete(nil, [XSNetwork errorWithCode:-1 message:@"订单校验失败!"]);
  613. }else{
  614. [XSNetwork showHudFailure:@"购买失败!"];
  615. complete(nil, [XSNetwork errorWithCode:-1 message:@"购买失败!"]);
  616. }
  617. }];
  618. }];
  619. }
  620. /// Pay a product with receipt. 客户端发送苹果的票据到服务器端校验.
  621. + (void)payWithReceipt:(id)receipt orderId:(id)orderId complete:(void(^)(id object, NSError* error))complete {
  622. // Prepare request parameters.
  623. id url = @"c";//"/api/p/v1/apple/p"
  624. id ps = @{
  625. @"data": receipt,
  626. @"orderId": orderId,
  627. @"uid": XenonSDK.sharedSDK.user.uid ?: @""
  628. };
  629. // Send a request to server.
  630. [self requestWithURL:url parameters:ps model:nil complete:complete];
  631. }
  632. #endif
  633. @end