pindo 4.6.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/bin/pindo +35 -0
  3. data/lib/pindo/base/aeshelper.rb +31 -0
  4. data/lib/pindo/base/deviceinfo.rb +90 -0
  5. data/lib/pindo/base/executable.rb +224 -0
  6. data/lib/pindo/base/githelper.rb +489 -0
  7. data/lib/pindo/base/hashhelper.rb +134 -0
  8. data/lib/pindo/base/plaininformative.rb +15 -0
  9. data/lib/pindo/base/plistbuddyexecutable.rb +121 -0
  10. data/lib/pindo/base/standarderror.rb +14 -0
  11. data/lib/pindo/client/applovinclient.rb +156 -0
  12. data/lib/pindo/client/aws3sclient.rb +183 -0
  13. data/lib/pindo/client/bossclient.rb +229 -0
  14. data/lib/pindo/client/bossconfigclient.rb +175 -0
  15. data/lib/pindo/client/giteeclient.rb +65 -0
  16. data/lib/pindo/client/httpclient.rb +40 -0
  17. data/lib/pindo/client/pgyerclient.rb +439 -0
  18. data/lib/pindo/client/tgateclient.rb +103 -0
  19. data/lib/pindo/command.rb +101 -0
  20. data/lib/pindo/config/pindoconfig.rb +439 -0
  21. data/lib/pindo/config/pindouserlocalconfig.rb +81 -0
  22. data/lib/pindo/deploy/Fastfile +233 -0
  23. data/lib/pindo/deploy/build.rb +167 -0
  24. data/lib/pindo/deploy/bundleid.rb +245 -0
  25. data/lib/pindo/deploy/cert.rb +508 -0
  26. data/lib/pindo/deploy/check.rb +79 -0
  27. data/lib/pindo/deploy/configproj.rb +89 -0
  28. data/lib/pindo/deploy/confusecode.rb +248 -0
  29. data/lib/pindo/deploy/confuseproj.rb +112 -0
  30. data/lib/pindo/deploy/fabric.rb +58 -0
  31. data/lib/pindo/deploy/getitcinfo.rb +248 -0
  32. data/lib/pindo/deploy/iap.rb +796 -0
  33. data/lib/pindo/deploy/initconfig.rb +128 -0
  34. data/lib/pindo/deploy/itcapp.rb +147 -0
  35. data/lib/pindo/deploy/itcinfo.rb +369 -0
  36. data/lib/pindo/deploy/pem.rb +55 -0
  37. data/lib/pindo/deploy/pullconfig.rb +46 -0
  38. data/lib/pindo/deploy/pushconfig.rb +83 -0
  39. data/lib/pindo/deploy/quswark.rb +146 -0
  40. data/lib/pindo/deploy/quswauth.rb +67 -0
  41. data/lib/pindo/deploy/reportbug.rb +135 -0
  42. data/lib/pindo/deploy/resign.rb +338 -0
  43. data/lib/pindo/deploy/tag.rb +108 -0
  44. data/lib/pindo/deploy/updateconfig.rb +129 -0
  45. data/lib/pindo/deploy/uploadipa.rb +63 -0
  46. data/lib/pindo/deploy.rb +44 -0
  47. data/lib/pindo/dev/autobuild.rb +204 -0
  48. data/lib/pindo/dev/autoresign.rb +186 -0
  49. data/lib/pindo/dev/confusecode.rb +127 -0
  50. data/lib/pindo/dev/confuseproj.rb +111 -0
  51. data/lib/pindo/dev/createbuild.rb +159 -0
  52. data/lib/pindo/dev/debug.rb +141 -0
  53. data/lib/pindo/dev/pub.rb +171 -0
  54. data/lib/pindo/dev/renewcert.rb +142 -0
  55. data/lib/pindo/dev.rb +23 -0
  56. data/lib/pindo/env/dreamstudio.rb +53 -0
  57. data/lib/pindo/env/flutter.rb +59 -0
  58. data/lib/pindo/env/flutter.sh +116 -0
  59. data/lib/pindo/env/quarkenv.rb +58 -0
  60. data/lib/pindo/env/swarkenv.rb +62 -0
  61. data/lib/pindo/env/workhard.rb +56 -0
  62. data/lib/pindo/env.rb +17 -0
  63. data/lib/pindo/ipa/autoresign.rb +188 -0
  64. data/lib/pindo/ipa/import.rb +261 -0
  65. data/lib/pindo/ipa/output.rb +396 -0
  66. data/lib/pindo/ipa.rb +22 -0
  67. data/lib/pindo/lib/forcepush.rb +159 -0
  68. data/lib/pindo/lib/lint.rb +115 -0
  69. data/lib/pindo/lib/push.rb +143 -0
  70. data/lib/pindo/lib/update.rb +75 -0
  71. data/lib/pindo/lib.rb +18 -0
  72. data/lib/pindo/module/appselect.rb +200 -0
  73. data/lib/pindo/module/appstore_in_app_purchase.rb +1803 -0
  74. data/lib/pindo/module/appstore_metadata_connect_api_helper.rb +12 -0
  75. data/lib/pindo/module/appstore_metadata_fastlane_helper.rb +922 -0
  76. data/lib/pindo/module/buildconfighelper.rb +13 -0
  77. data/lib/pindo/module/buildhelper.rb +76 -0
  78. data/lib/pindo/module/commonconfuseproj.rb +271 -0
  79. data/lib/pindo/module/config_project.sh +143 -0
  80. data/lib/pindo/module/configprojhelper.rb +631 -0
  81. data/lib/pindo/module/iap_tier.json +813 -0
  82. data/lib/pindo/module/icon_contents.json +116 -0
  83. data/lib/pindo/module/imessage_icon.json +91 -0
  84. data/lib/pindo/module/imgset_contents.json +21 -0
  85. data/lib/pindo/module/launchimg_contents.json +21 -0
  86. data/lib/pindo/module/pemcreate.rb +63 -0
  87. data/lib/pindo/module/pgyerhelper.rb +339 -0
  88. data/lib/pindo/module/xcodebuildpre.rb +258 -0
  89. data/lib/pindo/module/xcodehelper.rb +521 -0
  90. data/lib/pindo/options/appconfigoptions.rb +23 -0
  91. data/lib/pindo/options/deployoptions.rb +363 -0
  92. data/lib/pindo/pgyer/apptest.rb +104 -0
  93. data/lib/pindo/pgyer/comment.rb +95 -0
  94. data/lib/pindo/pgyer/download.rb +105 -0
  95. data/lib/pindo/pgyer/login.rb +61 -0
  96. data/lib/pindo/pgyer/upload.rb +234 -0
  97. data/lib/pindo/pgyer.rb +17 -0
  98. data/lib/pindo/repo/clone.rb +78 -0
  99. data/lib/pindo/repo/create.rb +55 -0
  100. data/lib/pindo/repo/login.rb +64 -0
  101. data/lib/pindo/repo/search.rb +60 -0
  102. data/lib/pindo/repo.rb +16 -0
  103. data/lib/pindo/setup.rb +60 -0
  104. data/lib/pindo/upgrade.rb +56 -0
  105. data/lib/pindo/utils/applovin.rb +213 -0
  106. data/lib/pindo/utils/boss.rb +209 -0
  107. data/lib/pindo/utils/clearcert.rb +60 -0
  108. data/lib/pindo/utils/device.rb +279 -0
  109. data/lib/pindo/utils/icon.rb +91 -0
  110. data/lib/pindo/utils/icon.sh +133 -0
  111. data/lib/pindo/utils/podindex.rb +56 -0
  112. data/lib/pindo/utils/podindex.sh +30 -0
  113. data/lib/pindo/utils/renewproj.rb +146 -0
  114. data/lib/pindo/utils/tgate.rb +159 -0
  115. data/lib/pindo/utils/xcassets.rb +46 -0
  116. data/lib/pindo/utils/xcassets.sh +88 -0
  117. data/lib/pindo/utils.rb +29 -0
  118. data/lib/pindo/version.rb +110 -0
  119. data/lib/pindo.rb +34 -0
  120. metadata +364 -0
@@ -0,0 +1,796 @@
1
+ require 'app_store_dev_api'
2
+ require 'json'
3
+ require 'faraday'
4
+
5
+
6
+ module Pindo
7
+ class Command
8
+ class Deploy < Command
9
+ class Iap < Deploy
10
+
11
+ include Command::DeployOptions
12
+
13
+ include AppStoreInAppPurchase
14
+
15
+ self.summary = '创建、修改AppStore中的Iap购买项,生成boss的iap购买项'
16
+
17
+ self.description = <<-DESC
18
+ 创建、修改AppStore中的Iap购买项,生成boss的iap购买项
19
+ DESC
20
+
21
+ self.arguments = [
22
+ CLAide::Argument.new('path/to/config.json', true),
23
+ ]
24
+
25
+ def self.options
26
+ [
27
+
28
+ ['--check', '检查iap信息'],
29
+ ['--download', '从appstore下载app的iap信息,用法: pindo deploy iap --download'],
30
+ ['--boss', '生成boss的iap信息, 用法: pindo deploy iap --boss'],
31
+
32
+ ].concat(super)
33
+ end
34
+
35
+ def validate!
36
+ super
37
+
38
+ pindo_dir = File::expand_path(pindo_single_config.pindo_dir)
39
+ config_repo_dir = File.join(pindo_dir, @bundle_id)
40
+ @iapconfig_dir =File.join(config_repo_dir, "fastlane/iap")
41
+
42
+ puts "iap dir: " + @iapconfig_dir
43
+ puts
44
+
45
+ end
46
+
47
+ def initialize(argv)
48
+
49
+ @check_flag = argv.flag?('check', false)
50
+ @download_flag = argv.flag?('download', false)
51
+ @boss_flag = argv.flag?('boss', false)
52
+
53
+ super
54
+ end
55
+
56
+ def run
57
+
58
+
59
+ if @download_flag
60
+
61
+ login_api_key(apple_id:@apple_id)
62
+ Spaceship::Tunes.login(@apple_id)
63
+
64
+ appstore_iap_file = File.join(@iapconfig_dir, "iap_appstore_download.json")
65
+ download_appstore_iap(appstore_iap_file:appstore_iap_file)
66
+ elsif @boss_flag
67
+
68
+ # puts "default boss vip auto =========\n\n\n"
69
+ # appstore_iap_file = File.join(@iapconfig_dir, "iap_appstore.json")
70
+ # produce_boss_iap(appstore_iap_file:appstore_iap_file, vip_mode:"autovip")
71
+
72
+ puts "one === first auto vip for boss =========\n\n\n"
73
+ appstore_iap_file = File.join(@iapconfig_dir, "one/iap_appstore.json")
74
+ produce_boss_iap(appstore_iap_file:appstore_iap_file, vip_mode:"autovip")
75
+
76
+ puts "two === second fancy vip for boss =========\n\n\n"
77
+ appstore_iap_file = File.join(@iapconfig_dir, "two/iap_appstore.json")
78
+ produce_boss_iap(appstore_iap_file:appstore_iap_file, vip_mode:"fancyvip")
79
+
80
+ puts "three === third funny vip for boss =========\n\n\n"
81
+ appstore_iap_file = File.join(@iapconfig_dir, "three/iap_appstore.json")
82
+ produce_boss_iap(appstore_iap_file:appstore_iap_file, vip_mode:"funnyvip")
83
+
84
+ else
85
+
86
+ login_api_key(apple_id:@apple_id)
87
+ Spaceship::Tunes.login(@apple_id)
88
+
89
+ create_iap_tester()
90
+ appstore_iap_file = File.join(@iapconfig_dir, "iap_appstore.json")
91
+ create_appstore_iap(appstore_iap_file:appstore_iap_file)
92
+ end
93
+ end
94
+
95
+ def download_appstore_iap(appstore_iap_file:nil)
96
+ puts "正在下载appstore iap 信息 ..."
97
+
98
+ app_list_response = @app_store_connect.list_apps(filter: {
99
+ bundle_id: @bundle_id
100
+ })
101
+
102
+ current_app = nil
103
+ app_list_response[:data].each do |app_info|
104
+ if app_info[:attributes][:bundle_id].to_s.eql?(@bundle_id)
105
+ current_app = app_info
106
+ end
107
+ end
108
+
109
+ app_id = current_app[:id]
110
+
111
+ puts " "
112
+ puts "app_id : #{app_id} +++++++++"
113
+
114
+ in_app_purchase_json = {}
115
+
116
+ puts "正在下载消耗型购买项..."
117
+ in_app_purchase_type = "CONSUMABLE"
118
+ in_app_purchase_json["consumable"] = get_consumable_iap_items(appstore_client:@app_store_connect, app_id:app_id, in_app_purchase_type:in_app_purchase_type) || []
119
+
120
+ puts "正在下载非消耗型购买项..."
121
+ in_app_purchase_type = "NON_CONSUMABLE"
122
+ in_app_purchase_json["non_consumable"] = get_consumable_iap_items(appstore_client:@app_store_connect, app_id:app_id, in_app_purchase_type:in_app_purchase_type) || []
123
+
124
+ puts "正在下载非自动订阅购买项..."
125
+ in_app_purchase_type = "NON_RENEWING_SUBSCRIPTION"
126
+ in_app_purchase_json["non_renew"] = get_consumable_iap_items(appstore_client:@app_store_connect, app_id:app_id, in_app_purchase_type:in_app_purchase_type) || []
127
+
128
+ puts "正在下载自动订阅购买项..."
129
+ subscription_group_items = get_subscriptions_groups(appstore_client:@app_store_connect, app_id:app_id)
130
+ in_app_purchase_json["auto_renew"] = subscription_group_items
131
+
132
+ puts JSON.pretty_generate(in_app_purchase_json)
133
+ File.open(appstore_iap_file, "w") do |f|
134
+ f.write(JSON.pretty_generate(in_app_purchase_json))
135
+ end
136
+
137
+ pindo_dir = File::expand_path(pindo_single_config.pindo_dir)
138
+ config_repo_dir = File.join(pindo_dir, @deploy_identifier)
139
+ git_addpush_repo(path:config_repo_dir, message:"download app iap", commit_file_params:["fastlane/iap/iap_appstore_download.json"])
140
+
141
+ update_app_share_secrets(app_id:app_id, need_create:false)
142
+ end
143
+
144
+ def produce_boss_iap(appstore_iap_file:nil, vip_mode:"autovip")
145
+
146
+ if !File.exist?(appstore_iap_file)
147
+
148
+ return
149
+ end
150
+
151
+ appstore_iap_file =File::expand_path(appstore_iap_file)
152
+ appstore_iap_json = JSON.parse(File.read(appstore_iap_file))
153
+
154
+ boss_iap_file =File.join(File::dirname(appstore_iap_file), "iap_boss.json")
155
+
156
+
157
+ if !appstore_iap_json.nil? && !appstore_iap_json["auto_renew"].nil?
158
+
159
+ appstore_iap_json["auto_renew"].each do |vip_group_item|
160
+ if !vip_group_item.nil?
161
+
162
+ appstore_iap_items = vip_group_item["items"] || vip_group_item["subscription_items"]
163
+
164
+ boss_iap_json = {}
165
+ boss_iap_json["client_vip"] = {}
166
+ boss_iap_json["offer_map"] = {}
167
+ boss_iap_json["client_vip"]["style"] = "tribble_style"
168
+ boss_iap_json["client_vip"]["best_deal"] = "offer.id." + vip_mode + "0"
169
+ boss_iap_json["client_vip"]["offer"] = []
170
+
171
+ if !appstore_iap_items.nil? && appstore_iap_items.size > 0
172
+
173
+ appstore_iap_items.each do |appstore_iap_item|
174
+
175
+ boss_iap_item = {}
176
+ boss_iap_item["offer_id"] = "offer.id." + vip_mode
177
+ boss_iap_item["kind"] = "vip"
178
+ boss_iap_item["way"] = "iap"
179
+ boss_iap_item["type"] = "auto_renewable_subscription"
180
+ boss_iap_item["subscription"] = {}
181
+
182
+ if !appstore_iap_item["subscription_free_trial"].nil? && appstore_iap_item["subscription_free_trial"].eql?("3d")
183
+ boss_iap_item["free_days"] = 3
184
+ end
185
+ boss_iap_item["iap"] = {}
186
+ boss_iap_item["iap"]["price"] = appstore_iap_item["price"]
187
+
188
+ if appstore_iap_item["subscription_duration"].include?("w")
189
+ boss_iap_item["offer_id"] = boss_iap_item["offer_id"] + "0"
190
+ boss_iap_item["subscription"]["period_type"] = "week"
191
+ boss_iap_item["subscription"]["period"] = appstore_iap_item["subscription_duration"].gsub('w', '').to_i
192
+ elsif appstore_iap_item["subscription_duration"].include?("m")
193
+ boss_iap_item["offer_id"] = boss_iap_item["offer_id"] + "1"
194
+ boss_iap_item["subscription"]["period_type"] = "month"
195
+ boss_iap_item["subscription"]["period"] = appstore_iap_item["subscription_duration"].gsub('m', '').to_i
196
+ boss_iap_item["iap"]["discount"] = 15
197
+ elsif appstore_iap_item["subscription_duration"].include?("y")
198
+ boss_iap_item["offer_id"] = boss_iap_item["offer_id"] + "2"
199
+ boss_iap_item["subscription"]["period_type"] = "year"
200
+ boss_iap_item["subscription"]["period"] = appstore_iap_item["subscription_duration"].gsub('y', '').to_i
201
+ boss_iap_item["iap"]["discount"] = 50
202
+ end
203
+
204
+ if !boss_iap_item["iap"]["price"].to_s.include?("$")
205
+ boss_iap_item["iap"]["price"] = "$" + boss_iap_item["iap"]["price"].to_s
206
+ end
207
+
208
+ boss_iap_item["iap"]["product_id"] = appstore_iap_item["product_id"]
209
+ if boss_iap_item["iap"]["product_id"].include?("PRODUCT_BUNDLE_IDENTIFIER")
210
+ temp_product_id = boss_iap_item["iap"]["product_id"]
211
+ temp_product_id = temp_product_id.gsub(/PRODUCT_BUNDLE_IDENTIFIER/, @bundle_id);
212
+ boss_iap_item["iap"]["product_id"] = temp_product_id
213
+ end
214
+
215
+ boss_iap_json["offer_map"][boss_iap_item["offer_id"]] = boss_iap_item
216
+ boss_iap_json["client_vip"]["offer"] << boss_iap_item["offer_id"]
217
+
218
+ end
219
+
220
+ end
221
+
222
+ puts JSON.pretty_generate(boss_iap_json)
223
+ puts "\n\n\n"
224
+ File.open(boss_iap_file, "w") do |f|
225
+ f.write(JSON.pretty_generate(boss_iap_json))
226
+ end
227
+
228
+
229
+ end
230
+ end
231
+ end
232
+
233
+ end
234
+
235
+
236
+ def update_app_share_secrets(app_id:nil, need_create:true)
237
+
238
+ app_secret_id = get_app_share_secrets(app_id:app_id, need_create:need_create)
239
+ if !app_secret_id.nil?
240
+ puts
241
+ puts "App Shared Secret: #{app_secret_id}"
242
+ puts
243
+ pindo_dir = File::expand_path(pindo_single_config.pindo_dir)
244
+ config_repo_dir = File.join(pindo_dir, @deploy_identifier)
245
+
246
+ app_origin_config = File.join(config_repo_dir, "config.json")
247
+ config_json = JSON.parse(File.read(app_origin_config))
248
+ config_json['app_info']['appstore_iap_secret'] = app_secret_id
249
+ File.open(app_origin_config, "w") do |file|
250
+ file.write(JSON.pretty_generate(config_json))
251
+ file.close
252
+ end
253
+
254
+ config_json = JSON.parse(File.read(@args_appconfig_fullname))
255
+ config_json['app_info']['appstore_iap_secret'] = app_secret_id
256
+ File.open(@args_appconfig_fullname, "w") do |file|
257
+ file.write(JSON.pretty_generate(config_json))
258
+ file.close
259
+ end
260
+
261
+ git_addpush_repo(path:config_repo_dir, message:"update app shared secret", commit_file_params:["config.json"])
262
+
263
+ end
264
+
265
+
266
+ end
267
+
268
+ def create_appstore_iap(appstore_iap_file:nil)
269
+
270
+ temp_iap_json = JSON.parse(File.read(appstore_iap_file))
271
+
272
+ if @check_flag
273
+ check_purchase_items_right(in_app_purchase_json:temp_iap_json, config_json:@config_json)
274
+ end
275
+
276
+ in_app_purchase_json = process_purchase_items(in_app_purchase_json:temp_iap_json, iap_tester:@iaptester)
277
+
278
+
279
+ app_list_response = @app_store_connect.list_apps(filter: {
280
+ bundle_id: @bundle_id
281
+ })
282
+
283
+
284
+ # puts app_list_response
285
+ current_app = nil
286
+
287
+ app_list_response[:data].each do |app_info|
288
+ if app_info[:attributes][:bundle_id].to_s.eql?(@bundle_id)
289
+ current_app = app_info
290
+ end
291
+ end
292
+
293
+ app_id = current_app[:id]
294
+
295
+ puts " "
296
+ puts "app_id : #{app_id} +++++++++"
297
+
298
+ puts " "
299
+ puts "创建消耗型购买项============="
300
+ create_consumable_iap_items(appstore_client:@app_store_connect, app_id:app_id, in_app_purchase_items:in_app_purchase_json["consumable"])
301
+
302
+ puts " "
303
+ puts "创建非消耗型购买项============="
304
+ create_consumable_iap_items(appstore_client:@app_store_connect, app_id:app_id, in_app_purchase_items:in_app_purchase_json["non_consumable"])
305
+
306
+ puts " "
307
+ puts "创建非续期订阅购买项============="
308
+ create_consumable_iap_items(appstore_client:@app_store_connect, app_id:app_id, in_app_purchase_items:in_app_purchase_json["non_renew"])
309
+
310
+ puts " "
311
+ puts "创建自动订阅购买项============="
312
+ create_subscriptions_groups(appstore_client:@app_store_connect, app_id:app_id, subscription_group_items:in_app_purchase_json["auto_renew"])
313
+
314
+ update_app_share_secrets(app_id:app_id)
315
+ end
316
+
317
+ def login_api_key (apple_id:nil)
318
+
319
+ load_apikey_config = false
320
+ api_key_json = nil
321
+ pindo_dir = File::expand_path(pindo_single_config.pindo_dir)
322
+ api_key_file = File.join(pindo_dir, "api_key.json")
323
+
324
+ if File.exist?(api_key_file)
325
+ api_key_json = JSON.parse(File.read(api_key_file))
326
+ if !api_key_json.nil? && !api_key_json[apple_id].nil? && !api_key_json[apple_id]["issuer_id"].nil?
327
+ issuer_id = api_key_json[apple_id]["issuer_id"]
328
+ key_id = api_key_json[apple_id]["key_id"]
329
+ private_key = api_key_json[apple_id]["private_key"]
330
+ load_apikey_config = true
331
+ end
332
+ end
333
+
334
+ if !load_apikey_config
335
+ issuer_id = ask('请输入 issuer_id: ')
336
+ key_id = ask('请输入 key_id: ')
337
+ private_key_file = ask('请输入private key的文件路径: ')
338
+ private_key = File.read(File.new(private_key_file))
339
+
340
+ api_key_json = api_key_json || {}
341
+ api_key_json[apple_id] = api_key_json[apple_id] || {}
342
+ api_key_json[apple_id]["issuer_id"] = issuer_id
343
+ api_key_json[apple_id]["key_id"] = key_id
344
+ api_key_json[apple_id]["private_key"] = private_key
345
+
346
+ answer = agree("请确认上面信息是否正确(Y/n):")
347
+ unless answer
348
+ raise Informative, "重新输入 !!!"
349
+ end
350
+
351
+ File.open(api_key_file, "w") do |f|
352
+ f.write(JSON.pretty_generate(api_key_json))
353
+ end
354
+ load_apikey_config = true
355
+
356
+
357
+ end
358
+
359
+ if load_apikey_config
360
+
361
+ puts "issuer_id : #{issuer_id}"
362
+ puts "key_id : #{key_id}"
363
+ puts "private_key : #{private_key}"
364
+
365
+ AppStoreDevApi.config = {
366
+ issuer_id: issuer_id,
367
+ key_id: key_id,
368
+ private_key: private_key
369
+ }
370
+ @app_store_connect = AppStoreDevApi::Client.new
371
+ end
372
+ end
373
+
374
+
375
+ def check_purchase_items_right(in_app_purchase_json:nil, config_json:nil)
376
+
377
+ puts
378
+ puts "检查iap 配置"
379
+ puts
380
+ app_version = @config_json["app_info"]["app_version"]
381
+ bundle_id = @bundle_id
382
+ platform = @config_json["app_setting"]["kGUKeyAppPlatform"] || @config_json["app_setting"]["app_platform"]
383
+ app_type = @config_json["project_info"]["app_type"]
384
+
385
+ boss_client = BossClient.new(app_type:app_type, is_dev:false)
386
+ data_json = Pindo::Command::Utils::Boss::get_config(boss_client:boss_client, app_version:app_version, bundle_id:bundle_id, platform:platform)
387
+
388
+ # if !data_json["data"].nil?
389
+ # puts JSON.pretty_generate(data_json["data"])
390
+ # else
391
+ # puts JSON.pretty_generate(data_json)
392
+ # end
393
+
394
+ gate_vip_items = get_gate_vip_items(app_type:app_type, boss_config:data_json["data"])
395
+
396
+ if !gate_vip_items.nil? && gate_vip_items.size > 0
397
+ itc_vip_items = []
398
+ in_app_purchase_json["auto_renew"].each do |group_item|
399
+ items = group_item["subscription_items"] || []
400
+ itc_vip_items += items
401
+ items = group_item["items"] || []
402
+ itc_vip_items += items
403
+ end
404
+
405
+
406
+ gate_vip_items.each do |offer_id, boss_iap_item|
407
+ boss_product_id = boss_iap_item["iap"]["product_id"]
408
+ boss_price = boss_iap_item["iap"]["price"]
409
+ free_days = boss_iap_item["free_days"]
410
+
411
+ itc_vip_item = itc_vip_items.find { |item| item["product_id"].eql?(boss_product_id)}
412
+ if !itc_vip_item.nil?
413
+ puts "product id: #{boss_product_id} 与boss一致!"
414
+
415
+ if boss_price.eql?("$"+itc_vip_item["price"].to_s)
416
+ puts " 价格一致! 价格:#{boss_price}"
417
+ else
418
+ local_price = itc_vip_item["price"].to_s
419
+ raise Informative, " 价格不一致 Boss:#{boss_price}, Local:$#{local_price}"
420
+ end
421
+
422
+ if !itc_vip_item["subscription_free_trial"].nil? && !free_days.nil? && itc_vip_item["subscription_free_trial"].to_s.eql?(free_days.to_s + "d")
423
+ puts " 有三天试用, 一致!"
424
+ elsif itc_vip_item["subscription_free_trial"].nil? && free_days.nil?
425
+ puts " 无三天试用, 一致!"
426
+ else
427
+ raise Informative, " 三天试用不一致!"
428
+ end
429
+
430
+
431
+ else
432
+ raise Informative, "product id: #{boss_product_id} 与boss不匹配!"
433
+ end
434
+ end
435
+ end
436
+
437
+ offer_vip_items = get_gate_vip_items(app_type:app_type, boss_config:data_json["data"])
438
+ end
439
+
440
+
441
+
442
+
443
+ def get_gate_vip_items(app_type:nil, boss_config:nil)
444
+ vip_items = nil
445
+
446
+ offer_ids = []
447
+ if !boss_config.nil? && !boss_config["content"].nil? && !boss_config["content"]["client"].nil? && !boss_config["content"]["client"]["vip"].nil? && !boss_config["content"]["client"]["vip"]["style_list"].nil?
448
+ style_list = boss_config["content"]["client"]["vip"]["style_list"] || {}
449
+ style_item = style_list.find { |item| item["style"].eql?("tribble_style")}
450
+ offer_ids = style_item["offer"] || []
451
+ end
452
+
453
+ if !boss_config.nil? && !boss_config["content"].nil? && !boss_config["content"]["common"].nil? && !boss_config["content"]["common"].nil? && !boss_config["content"]["common"]["offer_map"].nil?
454
+ boss_vip_items = boss_config["content"]["common"]["offer_map"]
455
+ vip_items = vip_items || {}
456
+ offer_ids.each do |offer_id|
457
+ if !boss_vip_items[offer_id].nil?
458
+ vip_items[offer_id] = boss_vip_items[offer_id]
459
+ end
460
+ end
461
+ end
462
+
463
+ return vip_items
464
+ end
465
+
466
+ def process_purchase_items(in_app_purchase_json:nil, iap_tester:nil)
467
+ new_in_app_purchase_json = {}
468
+ new_in_app_purchase_json["iaptester"] = @iaptester
469
+ new_in_app_purchase_json["bundle_id"] = @bundle_id
470
+
471
+ new_in_app_purchase_json["review_note"] = "username: " + iap_tester["email"] + " password: " + iap_tester["password"]
472
+
473
+ puts
474
+ puts
475
+ puts new_in_app_purchase_json["review_note"]
476
+
477
+ in_app_purchase_type = "CONSUMABLE"
478
+ items = process_consumable_iap_items(in_app_purchase_type:in_app_purchase_type, in_app_purchase_items:in_app_purchase_json["consumable"], iap_tester:iap_tester)
479
+ new_in_app_purchase_json["consumable"] = items
480
+
481
+ in_app_purchase_type = "NON_CONSUMABLE"
482
+ items = process_consumable_iap_items(in_app_purchase_type:in_app_purchase_type, in_app_purchase_items:in_app_purchase_json["non_consumable"], iap_tester:iap_tester)
483
+ new_in_app_purchase_json["non_consumable"] = items
484
+
485
+ in_app_purchase_type = "NON_RENEWING_SUBSCRIPTION"
486
+ items = process_consumable_iap_items(in_app_purchase_type:in_app_purchase_type, in_app_purchase_items:in_app_purchase_json["non_renew"], iap_tester:iap_tester)
487
+ new_in_app_purchase_json["non_renew"] = items
488
+
489
+ in_app_purchase_type = "Subscriptions"
490
+ new_subscrptions_json = process_subscriptions_json(subscription_json:in_app_purchase_json["auto_renew"], iap_tester:iap_tester)
491
+ new_in_app_purchase_json["auto_renew"] = new_subscrptions_json
492
+
493
+ File.open(File.join(@iapconfig_dir, "iap_log.json"), "w") do |f|
494
+ f.write(JSON.pretty_generate(new_in_app_purchase_json))
495
+ end
496
+
497
+ return new_in_app_purchase_json;
498
+
499
+ end
500
+
501
+ def process_consumable_iap_items(in_app_purchase_type:nil, in_app_purchase_items:nil, iap_tester:nil)
502
+
503
+ tier_json = get_price_tier_dict
504
+ iap_consumable_tier_dict = tier_json["consumable_price_tier"]
505
+
506
+ in_app_purchase_items = in_app_purchase_items || []
507
+ new_in_app_purchase_items = []
508
+ in_app_purchase_items.each do |purchase_item|
509
+ new_iap_items = {}
510
+ new_iap_items["reference_name"] = purchase_item["reference_name"].to_s.strip || purchase_item["product_name"].to_s.strip
511
+ if !purchase_item["reference_name"].nil? && purchase_item["reference_name"].length > 64
512
+ puts purchase_item["reference_name"]
513
+ puts "length: #{purchase_item["reference_name"].size}"
514
+ raise Informative, "Reference Name 超出限定的64个字符 !!!"
515
+ end
516
+
517
+ temp_product_id = purchase_item["product_id"].to_s.strip
518
+ new_iap_items["product_id"] = temp_product_id
519
+ if new_iap_items["product_id"].include?("PRODUCT_BUNDLE_IDENTIFIER")
520
+ temp_product_id = new_iap_items["product_id"]
521
+ temp_product_id = temp_product_id.gsub(/PRODUCT_BUNDLE_IDENTIFIER/, @bundle_id);
522
+ new_iap_items["product_id"] = temp_product_id
523
+ end
524
+ if !new_iap_items["product_id"].nil? && new_iap_items["product_id"].length > 100
525
+ puts temp_product_id
526
+ raise Informative, "product_id 超出限定的100个字符 !!!"
527
+ end
528
+
529
+ new_iap_items["in_app_purchase_type"] = in_app_purchase_type
530
+ new_iap_items["available_in_all_territories"] = true
531
+ new_iap_items["price"] = purchase_item["price"].to_s.strip
532
+ new_iap_items["price_tier"] = iap_consumable_tier_dict[new_iap_items["price"]]
533
+
534
+ if new_iap_items["price_tier"].nil?
535
+ raise "购买项价格错误 " + new_iap_items["product_id"] + " 价格 :" + purchase_item["price"]
536
+ end
537
+
538
+ new_iap_items["localization"] = {}
539
+ new_iap_items["localization"]["en-US"] = {}
540
+ new_iap_items["localization"]["en-US"]["locale"] = "en-US"
541
+ new_iap_items["localization"]["en-US"]["display_name"] = purchase_item["display_name"].to_s.strip
542
+
543
+ if !new_iap_items["localization"]["en-US"]["display_name"].nil? && new_iap_items["localization"]["en-US"]["display_name"].length > 30
544
+ puts temp_product_id
545
+ raise Informative, "Display Name 超出限定的30个字符 !!!"
546
+ end
547
+
548
+ new_iap_items["localization"]["en-US"]["description"] = purchase_item["description"].to_s.strip
549
+ if !new_iap_items["localization"]["en-US"]["description"].nil? && new_iap_items["localization"]["en-US"]["description"].length > 45
550
+ puts temp_product_id
551
+ raise Informative, "description 超出限定的45个字符 !!!"
552
+ end
553
+
554
+ new_iap_items["review_note"] = "username: " + iap_tester["email"] + " password: " + iap_tester["password"]
555
+ if !purchase_item["review_screenshot"].nil?
556
+ new_iap_items["review_screenshot"] = File.basename(purchase_item["review_screenshot"])
557
+ new_iap_items["review_screenshot_fullpath"] = File.join(@iapconfig_dir, new_iap_items["review_screenshot"])
558
+ else
559
+ new_iap_items["review_screenshot_fullpath"] = nil
560
+ end
561
+
562
+ new_in_app_purchase_items << new_iap_items
563
+ end
564
+
565
+ return new_in_app_purchase_items
566
+ end
567
+
568
+
569
+ def process_subscriptions_json(subscription_json:nil, iap_tester:nil)
570
+
571
+ tier_json = get_price_tier_dict
572
+ subscription_json = subscription_json || []
573
+ new_subscription_json = []
574
+ subscription_json.each do |subscription_group_item|
575
+ new_subscription_group_item = {}
576
+ new_subscription_group_item["group_reference_name"] = subscription_group_item["group_reference_name"].to_s.strip
577
+ new_subscription_group_item["localization"] = {}
578
+ new_subscription_group_item["localization"]["en-US"] = {}
579
+ new_subscription_group_item["localization"]["en-US"]["locale"] = "en-US"
580
+ new_subscription_group_item["localization"]["en-US"]["group_display_name"] = subscription_group_item["group_display_name"].to_s.strip
581
+
582
+ if !new_subscription_group_item["localization"]["en-US"]["group_display_name"].nil? && new_subscription_group_item["localization"]["en-US"]["group_display_name"].length > 45
583
+ puts new_subscription_group_item["localization"]["en-US"]["group_display_name"]
584
+ raise Informative, "group_display_name 超出限定的75个字符 !!!"
585
+ end
586
+ new_subscription_group_item["localization"]["en-US"]["custom_app_name"] = subscription_group_item["custom_app_name"].to_s.strip
587
+ if !new_subscription_group_item["localization"]["en-US"]["custom_app_name"].nil? && new_subscription_group_item["localization"]["en-US"]["custom_app_name"].length > 45
588
+ puts new_subscription_group_item["localization"]["en-US"]["custom_app_name"]
589
+ raise Informative, "custom_app_name 超出限定的30个字符 !!!"
590
+ end
591
+
592
+ new_subscription_group_item["subscription_items"] = []
593
+
594
+ old_subscription_group_items = subscription_group_item["subscription_items"] || []
595
+ if old_subscription_group_items.size <= 0 && !subscription_group_item["items"].nil?
596
+ old_subscription_group_items = subscription_group_item["items"] || []
597
+ end
598
+
599
+ new_subscription_group_item["subscription_items"] = process_subscriptions_item(subscription_items:old_subscription_group_items, iap_tester:iap_tester, subscription_duration_dict:tier_json["subscription_duration"])
600
+ new_subscription_json << new_subscription_group_item
601
+ end
602
+
603
+ return new_subscription_json
604
+ end
605
+
606
+ def process_subscriptions_item(subscription_items:nil, iap_tester:nil, subscription_duration_dict:nil)
607
+
608
+
609
+ subscription_items = subscription_items || []
610
+ new_subscription_items = []
611
+ subscription_items.each do |subs_item|
612
+ new_subs_item = {}
613
+
614
+ new_subs_item["reference_name"] = subs_item["reference_name"].to_s.strip || subs_item["product_name"].to_s.strip
615
+ if !new_subs_item["reference_name"].nil? && new_subs_item["reference_name"].length > 64
616
+ puts new_subs_item["reference_name"]
617
+ raise Informative, " Reference Name 超出限定的64个字符 !!!"
618
+ end
619
+
620
+ new_subs_item["product_id"] = subs_item["product_id"].to_s.strip
621
+ temp_product_id = new_subs_item["product_id"]
622
+ if new_subs_item["product_id"].include?("PRODUCT_BUNDLE_IDENTIFIER")
623
+ temp_product_id = new_subs_item["product_id"]
624
+ temp_product_id = temp_product_id.gsub(/PRODUCT_BUNDLE_IDENTIFIER/, @bundle_id);
625
+ new_subs_item["product_id"] = temp_product_id
626
+ end
627
+
628
+ if !new_subs_item["product_id"].nil? && new_subs_item["product_id"].length > 100
629
+ puts temp_product_id
630
+ raise Informative, "product_id 超出限定的100个字符 !!!"
631
+ end
632
+
633
+ new_subs_item["available_in_all_territories"] = true
634
+ new_subs_item["family_sharable"] = false
635
+ new_subs_item["price"] = subs_item["price"].to_s.strip
636
+ new_subs_item["subscription_duration"] = subscription_duration_dict[subs_item["subscription_duration"]]
637
+
638
+ if !subs_item["subscription_free_trial"].nil?
639
+ new_subs_item["subscription_free_trial"] = subs_item["subscription_free_trial"]
640
+ end
641
+ new_subs_item["localization"] = {}
642
+ new_subs_item["localization"]["en-US"] = {}
643
+ new_subs_item["localization"]["en-US"]["locale"] = "en-US"
644
+ new_subs_item["localization"]["en-US"]["display_name"] = subs_item["display_name"].to_s.strip
645
+ if !new_subs_item["localization"]["en-US"]["display_name"].nil? && new_subs_item["localization"]["en-US"]["display_name"].length > 30
646
+ puts temp_product_id
647
+ raise Informative, "Display Name 超出限定的30个字符 !!!"
648
+
649
+ end
650
+ new_subs_item["localization"]["en-US"]["description"] = subs_item["description"].to_s.strip
651
+ if !new_subs_item["localization"]["en-US"]["description"].nil? && new_subs_item["localization"]["en-US"]["description"].length > 45
652
+ puts temp_product_id
653
+ raise Informative, "Description 超出限定的45个字符 !!!"
654
+ end
655
+
656
+ new_subs_item["review_note"] = "username: " + iap_tester["email"] + " password: " + iap_tester["password"]
657
+ if !subs_item["review_screenshot"].nil?
658
+ new_subs_item["review_screenshot"] = File.basename(subs_item["review_screenshot"])
659
+ new_subs_item["review_screenshot_fullpath"] = File.join(@iapconfig_dir, new_subs_item["review_screenshot"])
660
+ else
661
+ new_subs_item["review_screenshot_fullpath"] = nil
662
+ end
663
+
664
+
665
+ new_subscription_items << new_subs_item
666
+ end
667
+
668
+ return new_subscription_items
669
+ end
670
+
671
+
672
+ def itc_has_sandbox_tester(email:nil)
673
+
674
+ has_sandbox_account = false
675
+ begin
676
+ response = @app_store_connect.list_sandbox_testers()
677
+ # puts JSON.pretty_generate(response)
678
+ has_sandbox_account = false
679
+ response[:data].each do |tester_item|
680
+ if tester_item[:attributes][:ac_account_name].to_s.eql?(@iaptester['email'])
681
+ has_sandbox_account = true
682
+ # puts "itc_has_sandbox_tester SandboxTester : " + @iaptester['email'] + "账号已经存在"
683
+ end
684
+ end
685
+ rescue => err
686
+
687
+ end
688
+
689
+ if !has_sandbox_account
690
+
691
+ begin
692
+ testers = Spaceship::ConnectAPI::SandboxTester.all()
693
+ if !testers.nil? && testers.size > 0
694
+ testers.each do | tester_item |
695
+ # puts tester_item.email
696
+ if tester_item.email.to_s.eql?(@iaptester['email'])
697
+ has_sandbox_account = true
698
+ # puts "itc_has_sandbox_tester ConnectAPI SandboxTester : " + @iaptester['email'] + "账号已经存在"
699
+ end
700
+ end
701
+ end
702
+ rescue => err
703
+
704
+ end
705
+
706
+ end
707
+
708
+ return has_sandbox_account
709
+ end
710
+
711
+
712
+
713
+ def create_iap_tester_ex( )
714
+ b_create_success = false
715
+ testers = nil
716
+ time = Time.local(1995 - rand(10), 12 - rand(10), 25- rand(20))
717
+ birth_date = time.strftime("%Y-%m-%d")
718
+ begin
719
+ testers = Spaceship::ConnectAPI::SandboxTester.create(
720
+ first_name: @iaptester['first_name'],
721
+ last_name: @iaptester['last_name'],
722
+ email: @iaptester['email'],
723
+ password: @iaptester['password'],
724
+ confirm_password: @iaptester['password'],
725
+ secret_question: @iaptester['password'] + '1234?',
726
+ secret_answer: @iaptester['password'],
727
+ birth_date:birth_date,
728
+ app_store_territory: @iaptester['territory'] || 'USA'
729
+ )
730
+ rescue => err
731
+ b_create_success = false
732
+ end
733
+
734
+ # if !b_create_success || testers.nil?
735
+ # begin
736
+ # testers = Spaceship::Tunes::SandboxTester.create!(
737
+ # email: @iaptester['email'],
738
+ # password: @iaptester['password'],
739
+ # first_name: @iaptester['first_name'],
740
+ # last_name: @iaptester['last_name'],
741
+ # country: @iaptester['territory'] || 'USA'
742
+ # )
743
+ # puts testers
744
+ # rescue => err
745
+
746
+ # end
747
+ # end
748
+
749
+ end
750
+
751
+
752
+ def create_iap_tester()
753
+
754
+ sandbox_tester_file = File.join(@iapconfig_dir, "iap_tester.json")
755
+ sandbox_tester_json = {}
756
+ if File.exist?(sandbox_tester_file)
757
+ sandbox_tester_json = JSON.parse(File.read(sandbox_tester_file))
758
+ else
759
+ raise Informative, "缺少沙盒配置文件iap_tester.json !!!"
760
+ end
761
+
762
+ @iaptester = nil
763
+ if !sandbox_tester_json.nil? && !sandbox_tester_json["iaptester"].nil?
764
+ @iaptester = sandbox_tester_json["iaptester"]
765
+ end
766
+
767
+ if !@iaptester.nil? && @iaptester["need_create"]
768
+ puts
769
+ puts
770
+ if itc_has_sandbox_tester(email:@iaptester["email"])
771
+
772
+ puts "已存在沙盒账号:#{@iaptester["email"]},无需创建!!!"
773
+ else
774
+ puts "没有沙盒账号:#{@iaptester["email"]},需新创建!!!"
775
+
776
+ create_iap_tester_ex()
777
+ if itc_has_sandbox_tester(email:@iaptester["email"])
778
+ puts "沙盒账号:#{@iaptester["email"]},创建成功!!!"
779
+ else
780
+ raise Informative, "沙盒账号:#{@iaptester["email"]},创建失败!!!"
781
+ end
782
+ end
783
+ else
784
+ raise Informative, "没有Sandbox Tester !!!"
785
+ end
786
+
787
+ end
788
+
789
+
790
+
791
+
792
+
793
+ end
794
+ end
795
+ end
796
+ end