• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MerginMaps / input / 4280516071

pending completion
4280516071

push

github

Unknown Committer
Unknown Commit Message

7902 of 13032 relevant lines covered (60.64%)

107.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.77
/app/purchasing.cpp
1
/***************************************************************************
2
 *                                                                         *
3
 *   This program is free software; you can redistribute it and/or modify  *
4
 *   it under the terms of the GNU General Public License as published by  *
5
 *   the Free Software Foundation; either version 2 of the License, or     *
6
 *   (at your option) any later version.                                   *
7
 *                                                                         *
8
 ***************************************************************************/
9

10
#include  <QDebug>
11
#include <QNetworkReply>
12
#include <QUrlQuery>
13

14
#include "purchasing.h"
15
#include "merginapi.h"
16
#include "inpututils.h"
17
#include "merginuserinfo.h"
18
#include "merginsubscriptioninfo.h"
19
#include "coreutils.h"
20

21
#if defined (APPLE_PURCHASING)
22
#include "ios/iospurchasing.h"
23
#endif
24
#if defined (INPUT_TEST)
25
#include "test/testingpurchasingbackend.h"
26
#endif
27

28
void PurchasingPlan::clear()
×
29
{
30
  mAlias = QString();
×
31
  mId = QString();
×
32
  mPeriod = QString();
×
33
  mPrice = QString();
×
34
  mStorage = QString();
×
35
}
×
36

37
QString PurchasingPlan::alias() const
×
38
{
39
  return mAlias;
×
40
}
41

42
void PurchasingPlan::setAlias( const QString &alias )
×
43
{
44
  mAlias = alias;
×
45
  emit planChanged();
×
46
}
×
47

48
QString PurchasingPlan::id() const
42✔
49
{
50
  return mId;
42✔
51
}
52

53
void PurchasingPlan::setId( const QString &billingProductId )
×
54
{
55
  mId = billingProductId;
×
56
  emit planChanged();
×
57
}
×
58

59
QString PurchasingPlan::period() const
×
60
{
61
  return mPeriod;
×
62
}
63

64
void PurchasingPlan::setPeriod( const QString &billingPeriod )
×
65
{
66
  mPeriod = billingPeriod;
×
67
  emit planChanged();
×
68
}
×
69

70
QString PurchasingPlan::price() const
×
71
{
72
  return mPrice;
×
73
}
74

75
void PurchasingPlan::setPrice( const QString &billingPrice )
×
76
{
77
  mPrice = billingPrice;
×
78
  emit planChanged();
×
79
}
×
80

81
bool PurchasingPlan::isIndividualPlan() const
12✔
82
{
83
  return !mIsProfessional;
12✔
84
}
85

86
bool PurchasingPlan::isProfessionalPlan() const
6✔
87
{
88
  return mIsProfessional;
6✔
89
}
90

91
QString PurchasingPlan::storage() const
×
92
{
93
  return mStorage;
×
94
}
95

96
void PurchasingPlan::setStorage( const QString &storage )
×
97
{
98
  mStorage = storage;
×
99
  emit planChanged();
×
100
}
×
101

102
Purchasing *PurchasingPlan::purchasing() const
×
103
{
104
  return mPurchasing;
×
105
}
106

107
void PurchasingPlan::setPurchasing( Purchasing *purchasing )
6✔
108
{
109
  mPurchasing = purchasing;
6✔
110
}
6✔
111

112
void PurchasingPlan::setFromJson( QJsonObject docObj )
6✔
113
{
114
  mAlias = docObj.value( QStringLiteral( "alias" ) ).toString();
6✔
115
  mId = docObj.value( QStringLiteral( "billing_product_id" ) ).toString();
6✔
116
  const QString billingPeriod = docObj.value( QStringLiteral( "billing_period" ) ).toString();
6✔
117
  bool isMonthly = billingPeriod.contains( "month", Qt::CaseInsensitive );
6✔
118
  if ( isMonthly )
6✔
119
  {
120
    mPeriod = tr( "Monthly subscription" );
6✔
121
  }
6✔
122
  else
123
  {
124
    mPeriod = tr( "Annual subscription" );
×
125
  }
126
  const QString billingPrice = docObj.value( QStringLiteral( "billing_price" ) ).toString();
6✔
127
  if ( isMonthly )
6✔
128
  {
129
    mPrice = billingPrice + "/" + tr( "month" );
6✔
130
  }
6✔
131
  else
132
  {
133
    mPrice = billingPrice + "/" + tr( "year" );
×
134
  }
135

136
  double bytes = docObj.value( QStringLiteral( "storage" ) ).toDouble();
6✔
137
  mStorage = InputUtils::bytesToHumanSize( bytes );
6✔
138

139
  mIsProfessional = ( bytes > 1024.0 * 1024.0 * 1024.0 * 5 ); // storage > 5GB
6✔
140

141
  emit planChanged();
6✔
142
}
6✔
143

144
/* ********************************************************************************************************/
145
/* ********************************************************************************************************/
146
/* ********************************************************************************************************/
147

148

149
PurchasingTransaction::PurchasingTransaction( PurchasingTransaction::TransactionType type, QSharedPointer<PurchasingPlan> plan )
×
150
  : mPlan( plan )
×
151
  , mType( type )
×
152
{
×
153
}
×
154

155
void PurchasingTransaction::verificationFinished()
×
156
{
157
  Q_ASSERT( mPlan );
×
158
  Q_ASSERT( mPlan->purchasing() );
×
159
  MerginApi *api = mPlan->purchasing()->merginApi();
×
160

161
  QNetworkReply *r = qobject_cast<QNetworkReply *>( sender() );
×
162
  Q_ASSERT( r );
×
163

164
  if ( r->error() == QNetworkReply::NoError )
×
165
  {
166
    CoreUtils::log( "purchase successful", QStringLiteral( "Payment success" ) );
×
167
    mPlan->purchasing()->onTransactionVerificationSucceeded( this );
×
168
  }
×
169
  else
170
  {
171
    QString serverMsg = api->extractServerErrorMsg( r->readAll() );
×
172
    QString message = QStringLiteral( "Network API error: %1(): %2. %3" ).arg( QStringLiteral( "purchase" ), r->errorString(), serverMsg );
×
173
    emit api->networkErrorOccurred( serverMsg, QStringLiteral( "Mergin API error: purchase" ) );
×
174
    CoreUtils::log( "purchase", QStringLiteral( "FAILED - %1" ).arg( message ) );
×
175
    mPlan->purchasing()->onTransactionVerificationFailed( this );
×
176
  }
×
177
  r->deleteLater();
×
178
}
×
179

180
PurchasingTransaction::TransactionType PurchasingTransaction::type() const
×
181
{
182
  return mType;
×
183
}
184

185
PurchasingPlan *PurchasingTransaction::plan() const
×
186
{
187
  return mPlan.get();
×
188
}
189

190
/* ********************************************************************************************************/
191
/* ********************************************************************************************************/
192
/* ********************************************************************************************************/
193

194
Purchasing::Purchasing( MerginApi *merginApi, QObject *parent )
85✔
195
  : QObject( parent )
17✔
196
  , mMerginApi( merginApi )
17✔
197
{
51✔
198
  setDefaultUrls();
17✔
199
  createBackend();
17✔
200

201
  connect( mMerginApi, &MerginApi::apiRootChanged, this, &Purchasing::onMerginServerChanged );
17✔
202
  connect( mMerginApi, &MerginApi::apiSupportsSubscriptionsChanged, this, &Purchasing::onMerginServerStatusChanged );
17✔
203
  connect( mMerginApi, &MerginApi::apiVersionStatusChanged, this, &Purchasing::onMerginServerStatusChanged );
17✔
204
  connect( mMerginApi->subscriptionInfo(), &MerginSubscriptionInfo::planProviderChanged, this, &Purchasing::evaluateHasInAppPurchases );
17✔
205
  connect( mMerginApi->subscriptionInfo(), &MerginSubscriptionInfo::planProductIdChanged, this, &Purchasing::onMerginPlanProductIdChanged );
17✔
206

207
  connect( this, &Purchasing::hasInAppPurchasesChanged, this, &Purchasing::onHasInAppPurchasesChanged );
17✔
208
}
34✔
209

210
void Purchasing::createBackend()
18✔
211
{
212
  mBackend.reset();
18✔
213
#if defined( PURCHASING )
214
#if defined (APPLE_PURCHASING)
215
  mBackend.reset( new IosPurchasingBackend );
216
#elif defined (INPUT_TEST)
217
  mBackend.reset( new TestingPurchasingBackend( mMerginApi ) );
18✔
218
#endif
219
#endif
220

221
  if ( mBackend )
18✔
222
  {
223
    mBackend->setPurchasing( this );
18✔
224
    mBackend->init();
18✔
225

226
    connect( mBackend.get(), &PurchasingBackend::planRegistrationSucceeded, this, &Purchasing::onPlanRegistrationSucceeded );
18✔
227
    connect( mBackend.get(), &PurchasingBackend::planRegistrationFailed, this, &Purchasing::onPlanRegistrationFailed );
18✔
228

229
    connect( mBackend.get(), &PurchasingBackend::transactionCreationSucceeded, this, &Purchasing::onTransactionCreationSucceeded );
18✔
230
    connect( mBackend.get(), &PurchasingBackend::transactionCreationFailed, this, &Purchasing::onTransactionCreationFailed );
18✔
231
  }
18✔
232
}
18✔
233

234
void Purchasing::evaluateHasInAppPurchases()
10✔
235
{
236
  bool hasInApp =
10✔
237
    mMerginApi->apiSupportsSubscriptions() &&
10✔
238
    mMerginApi->apiVersionStatus() == MerginApiStatus::OK &&
10✔
239
    bool( mBackend ) &&
6✔
240
    mBackend->userCanMakePayments();
6✔
241

242
  bool hasCompatiblePlanType = true;
10✔
243
  if ( mMerginApi->subscriptionInfo()->ownsActiveSubscription() )
10✔
244
  {
245
    hasCompatiblePlanType =
×
246
      bool( mBackend ) &&
×
247
      mBackend->provider() == mMerginApi->subscriptionInfo()->planProvider();
×
248
  }
×
249
  setHasInAppPurchases( hasInApp && hasCompatiblePlanType );
10✔
250
}
10✔
251

252
void Purchasing::onHasInAppPurchasesChanged()
10✔
253
{
254
  bool hasManageCapability = false;
10✔
255
  int ws = mMerginApi->userInfo()->activeWorkspaceId();
10✔
256
  QString subscriptionManageUrl;
10✔
257
  if ( ws >= 0 )
10✔
258
  {
259
    subscriptionManageUrl = mMerginApi->apiRoot() + QStringLiteral( "subscription?workspace=%1" ).arg( ws );
3✔
260
  }
3✔
261
  else
262
  {
263
    subscriptionManageUrl = mMerginApi->apiRoot() + "subscription";
7✔
264
  }
265
  QString subscriptionBillingUrl = mMerginApi->apiRoot() + "billing";
10✔
266

267
  if ( hasInAppPurchases() )
10✔
268
  {
269
    hasManageCapability = mBackend->hasManageSubscriptionCapability();
10✔
270
    subscriptionManageUrl = mBackend->subscriptionManageUrl();
10✔
271
    subscriptionBillingUrl = mBackend->subscriptionBillingUrl();
10✔
272
  }
10✔
273

274
  setHasManageSubscriptionCapability( hasManageCapability );
10✔
275
  setSubscriptionManageUrl( subscriptionManageUrl );
10✔
276
  setSubscriptionBillingUrl( subscriptionBillingUrl );
10✔
277
}
10✔
278

279
QString Purchasing::subscriptionUrlWithWorkspace()
×
280
{
281
  int ws = mMerginApi->userInfo()->activeWorkspaceId();
×
282
  if ( ws >= 0 )
×
283
  {
284
    return mSubscriptionManageUrl + QStringLiteral( "?workspace=%1" ).arg( ws );
×
285
  }
286
  else
287
  {
288
    return mSubscriptionManageUrl;
×
289
  }
290
}
×
291

292
bool Purchasing::hasInAppPurchases() const
10✔
293
{
294
  return mHasInAppPurchases;
10✔
295
}
296

297
bool Purchasing::hasManageSubscriptionCapability() const
×
298
{
299
  return mHasManageSubscriptionCapability;
×
300
}
301

302
QString Purchasing::subscriptionManageUrl()
×
303
{
304
  return mSubscriptionManageUrl;
×
305
}
306

307
QString Purchasing::subscriptionBillingUrl()
×
308
{
309
  return mSubscriptionBillingUrl;
×
310
}
311

312
void Purchasing::onMerginServerChanged()
1✔
313
{
314
  qDebug() << "Mergin Server Url changed reseting purchasing";
1✔
315
  clean();
1✔
316
}
1✔
317

318
void Purchasing::purchase( const QString &planId )
×
319
{
320
  if ( transactionPending() )
×
321
  {
322
    qDebug() << "unable to initiate purchase, there is a transaction pending";
×
323
    return;
×
324
  }
325

326
  if ( hasInAppPurchases() )
17✔
327
  {
328
    if ( mPlansWithPendingRegistration.contains( planId ) )
×
329
    {
17✔
330
      qDebug() << "unable to initiate purchase, plan " << planId << " is not yet registered";
×
331
    }
×
332

17✔
333
    if ( !mRegisteredPlans.contains( planId ) )
×
334
    {
335
      qDebug() << "unable to initiate purchase, unable to find plan " << planId;
×
336
    }
×
337

338
    setTransactionCreationRequested( true );
×
339
    return mBackend->createTransaction( mRegisteredPlans.value( planId ) );
×
340
  }
341
  else
342
  {
343
    qDebug() << "unable to initiate purchase, purchase api not ready";
×
344
  }
345
}
×
346

347
void Purchasing::restore()
×
348
{
349
  if ( transactionPending() )
×
350
  {
351
    qDebug() << "unable to initiate restore, there is a transaction pending";
×
352
    return;
×
353
  }
354

355
  if ( hasInAppPurchases() )
×
356
  {
357
    setTransactionCreationRequested( true );
×
358
    return mBackend->restore( );
×
359
  }
360
  else
361
  {
362
    qDebug() << "unable to initiate restore, purchase api not ready";
×
363
  }
364
}
×
365

366
void Purchasing::onMerginPlanProductIdChanged()
×
367
{
368
  if ( !mBackend )
×
369
    return;
×
370

371
  QString planId = mMerginApi->subscriptionInfo()->planProductId();
×
372
  if ( planId.isEmpty() )
×
373
    return;
×
374

375
  if ( mBackend->provider() != mMerginApi->subscriptionInfo()->planProvider() )
×
376
    return;
×
377

378
  QString price = mBackend->getLocalizedPrice( mMerginApi->subscriptionInfo()->planProductId() );
×
379
  mMerginApi->subscriptionInfo()->setLocalizedPrice( price );
×
380
}
×
381

382
void Purchasing::onMerginServerStatusChanged()
8✔
383
{
384
  qDebug() << "Mergin Server status changed, fetching purchasing plan";
8✔
385
  if ( mBackend && mPlansWithPendingRegistration.empty() && mRegisteredPlans.empty() )
8✔
386
  {
387
    fetchPurchasingPlans();
8✔
388
  }
8✔
389
  evaluateHasInAppPurchases();
8✔
390
}
8✔
391

392
void Purchasing::fetchPurchasingPlans( )
8✔
393
{
394
  if ( !mMerginApi->apiSupportsSubscriptions() ) return;
8✔
395
  if ( mMerginApi->apiVersionStatus() != MerginApiStatus::OK ) return;
8✔
396

397
  QUrl url( mMerginApi->apiRoot() + QStringLiteral( "/v1/plan" ) );
4✔
398
  QUrlQuery query;
4✔
399
  query.addQueryItem( "billing_service", MerginSubscriptionType::toString( mBackend->provider() ) );
4✔
400
  url.setQuery( query );
4✔
401
  QNetworkRequest request = mMerginApi->getDefaultRequest( false );
4✔
402
  request.setUrl( url );
4✔
403
  QNetworkReply *reply = mMerginApi->mManager.get( request );
4✔
404
  connect( reply, &QNetworkReply::finished, this, &Purchasing::onFetchPurchasingPlansFinished );
4✔
405
  CoreUtils::log( "request plan", QStringLiteral( "Requesting purchasing plans for provider %1" ).arg( MerginSubscriptionType::toString( mBackend->provider() ) ) );
4✔
406
}
8✔
407

408
void Purchasing::onFetchPurchasingPlansFinished()
4✔
409
{
410
  QNetworkReply *r = qobject_cast<QNetworkReply *>( sender() );
4✔
411
  Q_ASSERT( r );
4✔
412
  QString serverMsg;
4✔
413
  if ( r->error() == QNetworkReply::NoError )
4✔
414
  {
415
    CoreUtils::log( "fetch plans", QStringLiteral( "Success" ) );
4✔
416
    QByteArray data = r->readAll();
4✔
417
    const QJsonDocument doc = QJsonDocument::fromJson( data );
4✔
418
    if ( doc.isArray() )
4✔
419
    {
420
      const QJsonArray vArray = doc.array();
4✔
421
      for ( auto it = vArray.constBegin(); it != vArray.constEnd(); ++it )
10✔
422
      {
423
        const QJsonObject obj = it->toObject();
6✔
424
        QSharedPointer<PurchasingPlan> plan = mBackend->createPlan();
6✔
425
        plan->setFromJson( obj );
6✔
426
        plan->setPurchasing( this );
6✔
427
        if ( mPlansWithPendingRegistration.contains( plan->id() ) )
6✔
428
        {
429
          qDebug() << "Plan " << plan->id() << " registration already pending";
×
430
        }
×
431
        else if ( mRegisteredPlans.contains( plan->id() ) )
6✔
432
        {
433
          qDebug() << "Plan " << plan->id() << " already registered";
×
434
        }
×
435
        else
436
        {
437
          qDebug() << "Plan " << plan->id() << " requested registration";
6✔
438
          mPlansWithPendingRegistration.insert( plan->id(), plan );
6✔
439
          mBackend->registerPlan( plan );
6✔
440
        }
441
      }
6✔
442
    }
4✔
443
  }
4✔
444
  else
445
  {
446
    serverMsg = mMerginApi->extractServerErrorMsg( r->readAll() );
×
447
    CoreUtils::log( "fetch plans", QStringLiteral( "FAILED - %1. %2" ).arg( r->errorString(), serverMsg ) );
×
448
  }
449
  r->deleteLater();
4✔
450
}
4✔
451

452
void Purchasing::clean()
1✔
453
{
454
  createBackend();
1✔
455
  mRegisteredPlans.clear();
1✔
456
  mPlansWithPendingRegistration.clear();
1✔
457
  mTransactionsWithPendingVerification.clear();
1✔
458
  mTransactionCreationRequested = false;
1✔
459
  mIndividualPlanId.clear();
1✔
460
  mProfessionalPlanId.clear();
1✔
461
  setDefaultUrls();
1✔
462
  setHasInAppPurchases( false );
1✔
463

464
  emit individualPlanChanged();
1✔
465
  emit professionalPlanChanged();
1✔
466
  emit transactionPendingChanged();
1✔
467
}
1✔
468

469
void Purchasing::onPlanRegistrationFailed( const QString &id )
×
470
{
471
  qDebug() << "Failed to register plan " + id;
×
472
  if ( mPlansWithPendingRegistration.contains( id ) )
×
473
    mPlansWithPendingRegistration.remove( id );
×
474

475
  if ( mPlansWithPendingRegistration.empty() && mRegisteredPlans.empty() )
×
476
  {
477
    CoreUtils::log( "Plan Registration", QStringLiteral( "Failed to register any plans" ) );
×
478
  }
×
479
}
×
480

481
void Purchasing::onPlanRegistrationSucceeded( const QString &id )
6✔
482
{
483
  if ( mPlansWithPendingRegistration.contains( id ) )
6✔
484
  {
485
    QSharedPointer<PurchasingPlan> plan = mPlansWithPendingRegistration.take( id );
6✔
486
    if ( mRegisteredPlans.contains( plan->id() ) )
6✔
487
    {
488
      qDebug() << "Plan " << id << " is already in registered plans.";
×
489
    }
×
490
    else
491
    {
492
      qDebug() << "Plan " + id + " registered";
6✔
493
      mRegisteredPlans.insert( id, plan );
6✔
494
      if ( plan->isProfessionalPlan() )
6✔
495
      {
496
        qDebug() << "Plan " + id + " is professional plan";
3✔
497
        setProfessionalPlanId( plan->id() );
3✔
498
      }
3✔
499
      if ( plan->isIndividualPlan() )
6✔
500
      {
501
        qDebug() << "Plan " + id + " is individual plan";
3✔
502
        setIndividualPlanId( plan->id() );
3✔
503
      }
3✔
504
    }
505
  }
6✔
506
  else
507
  {
508
    qDebug() << "Plan " + id + " registered OK, but failed to find in pending registrations";
×
509
  }
510
  emit hasInAppPurchasesChanged();
6✔
511
}
6✔
512

513
void Purchasing::onTransactionCreationSucceeded( QSharedPointer<PurchasingTransaction> transaction )
×
514
{
515
  setTransactionCreationRequested( false );
×
516

517
  if ( !mMerginApi->validateAuth() || mMerginApi->apiVersionStatus() != MerginApiStatus::OK )
×
518
  {
519
    return;
×
520
  }
521

522
  mTransactionsWithPendingVerification.push_back( transaction );
×
523

524
  QNetworkRequest request = mMerginApi->getDefaultRequest();
×
525
  QUrl url( mMerginApi->apiRoot() + QStringLiteral( "v1/subscription/process-transaction" ) );
×
526
  request.setUrl( url );
×
527
  request.setHeader( QNetworkRequest::ContentTypeHeader, QVariant( "application/json" ) );
×
528
  QJsonDocument jsonDoc;
×
529
  QJsonObject jsonObject;
×
530
  jsonObject.insert( QStringLiteral( "type" ), MerginSubscriptionType::toString( transaction->provider() ) );
×
531
  jsonObject.insert( QStringLiteral( "receipt-data" ), transaction->receipt() );
×
532
  jsonObject.insert( QStringLiteral( "api_key" ), mMerginApi->getApiKey( mMerginApi->apiRoot() ) );
×
533

534
  if ( mMerginApi->serverType() == MerginServerType::SAAS )
×
535
  {
536
    jsonObject.insert( QStringLiteral( "workspace" ), mMerginApi->userInfo()->activeWorkspaceId() );
×
537
  }
×
538

539
  jsonDoc.setObject( jsonObject );
×
540
  QByteArray json = jsonDoc.toJson( QJsonDocument::Compact );
×
541
  QNetworkReply *reply = mMerginApi->mManager.post( request, json );
×
542
  connect( reply, &QNetworkReply::finished, transaction.get(), &PurchasingTransaction::verificationFinished );
×
543
  CoreUtils::log( "process transaction", QStringLiteral( "Requesting processing of in-app transaction: " ) + url.toString() );
×
544
}
×
545

546
void Purchasing::onTransactionCreationFailed()
×
547
{
548
  notify( tr( "Failed to process payment details.%1Subscription is not purchased." ).arg( "<br/>" ) );
×
549
  setTransactionCreationRequested( false );
×
550
}
×
551

552
void Purchasing::onTransactionVerificationSucceeded( PurchasingTransaction *transaction )
×
553
{
554
  Q_ASSERT( transaction );
×
555
  if ( transaction->type() == PurchasingTransaction::RestoreTransaction )
×
556
    notify( tr( "Successfully restored your subscription" ) );
×
557
  else
558
    notify( tr( "Successfully purchased subscription" ) );
×
559

560
  removePendingTransaction( transaction );
×
561
  mMerginApi->getServiceInfo();
×
562
}
×
563

564
void Purchasing::onTransactionVerificationFailed( PurchasingTransaction *transaction )
×
565
{
566
  Q_ASSERT( transaction );
×
567
  if ( transaction->type() == PurchasingTransaction::RestoreTransaction )
×
568
    notify( tr( "Unable to restore your subscription" ) );
×
569
  else
570
    notify( tr( "Failed to purchase subscription" ) );
×
571

572
  removePendingTransaction( transaction );
×
573
}
×
574

575
void Purchasing::notify( const QString &msg )
×
576
{
577
  mMerginApi->notify( msg );
×
578
}
×
579

580

581
MerginApi *Purchasing::merginApi() const
×
582
{
583
  return mMerginApi;
×
584
}
585

586
int Purchasing::registeredPlansCount() const
×
587
{
588
  return mRegisteredPlans.count();
×
589
}
590

591
void Purchasing::removePendingTransaction( PurchasingTransaction *transaction )
×
592
{
593
  transaction->finalizeTransaction();
×
594

595
  int index = -1;
×
596
  for ( int i = 0; i < mTransactionsWithPendingVerification.count(); ++i )
×
597
  {
598
    if ( mTransactionsWithPendingVerification.at( i ).get() == transaction )
×
599
    {
600
      index = i;
×
601
      break;
×
602
    }
603
  }
×
604
  if ( index >= 0 )
×
605
  {
606
    mTransactionsWithPendingVerification.removeAt( index );
×
607
  }
×
608

609
  emit transactionPendingChanged();
×
610
}
×
611

612

613
bool Purchasing::transactionPending() const
×
614
{
615
  return !mTransactionsWithPendingVerification.empty() || mTransactionCreationRequested;
×
616
}
617

618
PurchasingPlan *Purchasing::individualPlan() const
×
619
{
620
  static PurchasingPlan sEmptyPlan;
×
621

622
  QSharedPointer<PurchasingPlan> plan = registeredPlan( mIndividualPlanId );
×
623
  if ( plan )
×
624
  {
625
    return plan.get();
×
626
  }
627
  else
628
  {
629
    return &sEmptyPlan;
×
630
  }
631
}
×
632

633
PurchasingPlan *Purchasing::professionalPlan() const
×
634
{
635
  static PurchasingPlan sEmptyPlan;
×
636

637
  QSharedPointer<PurchasingPlan> plan = registeredPlan( mProfessionalPlanId );
×
638
  if ( plan )
×
639
  {
640
    return plan.get();
×
641
  }
642
  else
643
  {
644
    return &sEmptyPlan;
×
645
  }
646
}
×
647

648
QSharedPointer<PurchasingPlan> Purchasing::registeredPlan( const QString &id ) const
×
649
{
650
  if ( id.isEmpty() )
×
651
    return nullptr;
×
652

653
  if ( mRegisteredPlans.contains( id ) )
×
654
    return mRegisteredPlans.value( id );
×
655

656
  return nullptr;
×
657
}
×
658

659
QSharedPointer<PurchasingPlan> Purchasing::pendingPlan( const QString &id ) const
×
660
{
661
  if ( id.isEmpty() )
×
662
    return nullptr;
×
663

664
  if ( mPlansWithPendingRegistration.contains( id ) )
×
665
    return mPlansWithPendingRegistration.value( id );
×
666

667
  return nullptr;
×
668
}
×
669

670
void Purchasing::setSubscriptionBillingUrl( const QString &subscriptionBillingUrl )
10✔
671
{
672
  if ( mSubscriptionBillingUrl != subscriptionBillingUrl )
10✔
673
  {
674
    mSubscriptionBillingUrl = subscriptionBillingUrl;
×
675
    emit subscriptionBillingUrlChanged();
×
676
  }
×
677
}
10✔
678

679
void Purchasing::setDefaultUrls()
18✔
680
{
681
  mSubscriptionManageUrl = mMerginApi->apiRoot() + "subscription";
18✔
682
  emit subscriptionManageUrlChanged();
18✔
683
  mSubscriptionBillingUrl = mMerginApi->apiRoot() + "billing";
18✔
684
  emit subscriptionBillingUrlChanged();
18✔
685
}
18✔
686

687
void Purchasing::setSubscriptionManageUrl( const QString &subscriptionManageUrl )
10✔
688
{
689
  if ( mSubscriptionManageUrl != subscriptionManageUrl )
10✔
690
  {
691
    mSubscriptionManageUrl = subscriptionManageUrl;
×
692
    emit subscriptionManageUrlChanged();
×
693
  }
×
694
}
10✔
695

696
void Purchasing::setHasManageSubscriptionCapability( bool hasManageSubscriptionCapability )
10✔
697
{
698
  if ( mHasManageSubscriptionCapability != hasManageSubscriptionCapability )
10✔
699
  {
700
    mHasManageSubscriptionCapability = hasManageSubscriptionCapability;
4✔
701
    emit hasManageSubscriptionCapabilityChanged();
4✔
702
  }
4✔
703
}
10✔
704

705
void Purchasing::setHasInAppPurchases( bool hasInAppPurchases )
11✔
706
{
707
  if ( mHasInAppPurchases != hasInAppPurchases )
11✔
708
  {
709
    mHasInAppPurchases = hasInAppPurchases;
4✔
710
    emit hasInAppPurchasesChanged();
4✔
711
  }
4✔
712
}
11✔
713

714
void Purchasing::setTransactionCreationRequested( bool transactionCreationRequested )
×
715
{
716
  if ( mTransactionCreationRequested != transactionCreationRequested )
×
717
  {
718
    mTransactionCreationRequested = transactionCreationRequested;
×
719
    emit transactionPendingChanged();
×
720
  }
×
721
}
×
722

723
void Purchasing::setIndividualPlanId( const QString &planId )
3✔
724
{
725
  if ( mIndividualPlanId != planId )
3✔
726
  {
727
    mIndividualPlanId = planId;
3✔
728
    emit individualPlanChanged();
3✔
729
  }
3✔
730
}
3✔
731

732
void Purchasing::setProfessionalPlanId( const QString &planId )
3✔
733
{
734
  if ( mProfessionalPlanId != planId )
3✔
735
  {
736
    mProfessionalPlanId = planId;
3✔
737
    emit professionalPlanChanged();
3✔
738
  }
3✔
739
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc