Skip to content

Commit 8af2655

Browse files
committedSep 28, 2021
OctopusDeploy release: 1.7.44
1 parent 8e5e182 commit 8af2655

File tree

14 files changed

+1056
-51
lines changed

14 files changed

+1056
-51
lines changed
 

‎CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212

1313
#### Enhancements
1414

15+
- Add Multiple merchants to GpApi
16+
17+
---
18+
19+
## v1.7.43 (09/23/2021)
20+
21+
#### Enhancements
22+
1523
- Add Entry Mode to GpApi
1624

1725
---

‎examples/webforms/end-to-end/Properties/AssemblyInfo.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@
3131
//
3232
// You can specify all the values or you can default the Revision and Build Numbers
3333
// by using the '*' as shown below:
34-
[assembly: AssemblyVersion("1.8.4")]
35-
[assembly: AssemblyFileVersion("1.8.4")]
34+
[assembly: AssemblyVersion("1.8.5")]
35+
[assembly: AssemblyFileVersion("1.8.5")]

‎src/GlobalPayments.Api/Builders/AuthorizationBuilder.cs

-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ public class AuthorizationBuilder : TransactionBuilder<Transaction> {
8585
internal string CheckCustomerId { get; set; }
8686
internal string RawMICRData { get; set; }
8787
internal StoredCredentialInitiator? TransactionInitiator { get; set; }
88-
8988
internal bool HasEmvFallbackData {
9089
get {
9190
return (EmvFallbackCondition != null || EmvLastChipRead != null || !string.IsNullOrEmpty(PaymentApplicationVersion));

‎src/GlobalPayments.Api/Builders/ManagementBuilder.cs

-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ internal string TransactionId {
8080
internal bool AllowDuplicates { get; set; }
8181
internal CardHolderAuthenticationMethod? AuthenticationMethod { get; set; }
8282
internal string TagData { get; set; }
83-
8483
//internal string EWICIssuingEntity { get; set; }
8584
//internal CustomerData AuthorizationCustomerData { get; set; }
8685

‎src/GlobalPayments.Api/Builders/Secure3dBuilder.cs

-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ internal Secure3dVersion? Version {
102102
internal bool? WhitelistStatus { get; set; }
103103
internal string WorkCountryCode { get; set; }
104104
internal string WorkNumber { get; set; }
105-
106105
public Secure3dBuilder WithAddress(Address address) {
107106
return WithAddress(address, AddressType.Billing);
108107
}

‎src/GlobalPayments.Api/Entities/GpApi/GpApiAuthorizationRequestBuilder.cs

+6-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace GlobalPayments.Api.Entities {
99
internal class GpApiAuthorizationRequestBuilder {
1010
internal static GpApiRequest BuildRequest(AuthorizationBuilder builder, GpApiConnector gateway) {
11+
var merchantUrl = !string.IsNullOrEmpty(gateway.MerchantId) ? $"/merchants/{gateway.MerchantId}" : string.Empty;
1112
var paymentMethod = new JsonDoc()
1213
.Set("entry_mode", GetEntryMode(builder, gateway.Channel)); // [MOTO, ECOM, IN_APP, CHIP, SWIPE, MANUAL, CONTACTLESS_CHIP, CONTACTLESS_SWIPE]
1314
if (builder.PaymentMethod is CreditCardData && (builder.TransactionModifier == TransactionModifier.EncryptedMobile || builder.TransactionModifier == TransactionModifier.DecryptedMobile))
@@ -69,7 +70,7 @@ internal static GpApiRequest BuildRequest(AuthorizationBuilder builder, GpApiCon
6970

7071
return new GpApiRequest {
7172
Verb = HttpMethod.Post,
72-
Endpoint = "/payment-methods",
73+
Endpoint = $"{merchantUrl}/payment-methods",
7374
RequestBody = tokenizationData.ToString(),
7475
};
7576
}
@@ -84,7 +85,7 @@ internal static GpApiRequest BuildRequest(AuthorizationBuilder builder, GpApiCon
8485

8586
return new GpApiRequest {
8687
Verb = HttpMethod.Post,
87-
Endpoint = "/payment-methods",
88+
Endpoint = $"{merchantUrl}/payment-methods",
8889
RequestBody = tokenizationData.ToString(),
8990
};
9091
}
@@ -107,7 +108,7 @@ internal static GpApiRequest BuildRequest(AuthorizationBuilder builder, GpApiCon
107108

108109
return new GpApiRequest {
109110
Verb = HttpMethod.Post,
110-
Endpoint = "/verifications",
111+
Endpoint = $"{merchantUrl}/verifications",
111112
RequestBody = verificationData.ToString(),
112113
};
113114
}
@@ -136,7 +137,7 @@ internal static GpApiRequest BuildRequest(AuthorizationBuilder builder, GpApiCon
136137

137138
return new GpApiRequest {
138139
Verb = HttpMethod.Post,
139-
Endpoint = "/verifications",
140+
Endpoint = $"{merchantUrl}/verifications",
140141
RequestBody = verificationData.ToString(),
141142
};
142143
}
@@ -268,7 +269,7 @@ internal static GpApiRequest BuildRequest(AuthorizationBuilder builder, GpApiCon
268269

269270
return new GpApiRequest {
270271
Verb = HttpMethod.Post,
271-
Endpoint = "/transactions",
272+
Endpoint = $"{merchantUrl}/transactions",
272273
RequestBody = data.ToString(),
273274
};
274275
}

‎src/GlobalPayments.Api/Entities/GpApi/GpApiManagementRequestBuilder.cs

+10-9
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
namespace GlobalPayments.Api.Entities {
88
internal class GpApiManagementRequestBuilder {
99
internal static GpApiRequest BuildRequest(ManagementBuilder builder, GpApiConnector gateway) {
10+
var merchantUrl = !string.IsNullOrEmpty(gateway.MerchantId) ? $"/merchants/{gateway.MerchantId}" : string.Empty;
1011
if (builder.TransactionType == TransactionType.Capture) {
1112
var data = new JsonDoc()
1213
.Set("amount", builder.Amount.ToNumericCurrencyString())
1314
.Set("gratuity_amount", builder.Gratuity.ToNumericCurrencyString());
1415

1516
return new GpApiRequest {
1617
Verb = HttpMethod.Post,
17-
Endpoint = $"/transactions/{builder.TransactionId}/capture",
18+
Endpoint = $"{merchantUrl}/transactions/{builder.TransactionId}/capture",
1819
RequestBody = data.ToString(),
1920
};
2021
}
@@ -24,7 +25,7 @@ internal static GpApiRequest BuildRequest(ManagementBuilder builder, GpApiConnec
2425

2526
return new GpApiRequest {
2627
Verb = HttpMethod.Post,
27-
Endpoint = $"/transactions/{builder.TransactionId}/refund",
28+
Endpoint = $"{merchantUrl}/transactions/{builder.TransactionId}/refund",
2829
RequestBody = data.ToString(),
2930
};
3031
}
@@ -34,7 +35,7 @@ internal static GpApiRequest BuildRequest(ManagementBuilder builder, GpApiConnec
3435

3536
return new GpApiRequest {
3637
Verb = HttpMethod.Post,
37-
Endpoint = $"/transactions/{builder.TransactionId}/reversal",
38+
Endpoint = $"{merchantUrl}/transactions/{builder.TransactionId}/reversal",
3839
RequestBody = data.ToString(),
3940
};
4041
}
@@ -50,20 +51,20 @@ internal static GpApiRequest BuildRequest(ManagementBuilder builder, GpApiConnec
5051

5152
return new GpApiRequest {
5253
Verb = new HttpMethod("PATCH"),
53-
Endpoint = $"/payment-methods/{(builder.PaymentMethod as ITokenizable).Token}",
54+
Endpoint = $"{merchantUrl}/payment-methods/{(builder.PaymentMethod as ITokenizable).Token}",
5455
RequestBody = data.ToString(),
5556
};
5657
}
5758
else if (builder.TransactionType == TransactionType.TokenDelete && builder.PaymentMethod is ITokenizable) {
5859
return new GpApiRequest {
5960
Verb = HttpMethod.Delete,
60-
Endpoint = $"/payment-methods/{(builder.PaymentMethod as ITokenizable).Token}",
61+
Endpoint = $"{merchantUrl}/payment-methods/{(builder.PaymentMethod as ITokenizable).Token}",
6162
};
6263
}
6364
else if (builder.TransactionType == TransactionType.DisputeAcceptance) {
6465
return new GpApiRequest {
6566
Verb = HttpMethod.Post,
66-
Endpoint = $"/disputes/{builder.DisputeId}/acceptance",
67+
Endpoint = $"{merchantUrl}/disputes/{builder.DisputeId}/acceptance",
6768
};
6869
}
6970
else if (builder.TransactionType == TransactionType.DisputeChallenge) {
@@ -72,14 +73,14 @@ internal static GpApiRequest BuildRequest(ManagementBuilder builder, GpApiConnec
7273

7374
return new GpApiRequest {
7475
Verb = HttpMethod.Post,
75-
Endpoint = $"/disputes/{builder.DisputeId}/challenge",
76+
Endpoint = $"{merchantUrl}/disputes/{builder.DisputeId}/challenge",
7677
RequestBody = data.ToString(),
7778
};
7879
}
7980
else if (builder.TransactionType == TransactionType.BatchClose) {
8081
return new GpApiRequest {
8182
Verb = HttpMethod.Post,
82-
Endpoint = $"/batches/{builder.BatchReference}",
83+
Endpoint = $"{merchantUrl}/batches/{builder.BatchReference}",
8384
};
8485
}
8586
else if (builder.TransactionType == TransactionType.Reauth) {
@@ -88,7 +89,7 @@ internal static GpApiRequest BuildRequest(ManagementBuilder builder, GpApiConnec
8889

8990
return new GpApiRequest {
9091
Verb = HttpMethod.Post,
91-
Endpoint = $"/transactions/{builder.TransactionId}/reauthorization",
92+
Endpoint = $"{merchantUrl}/transactions/{builder.TransactionId}/reauthorization",
9293
RequestBody = data.ToString(),
9394
};
9495
}

‎src/GlobalPayments.Api/Entities/GpApi/GpApiReportRequestBuilder.cs

+14-13
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@
66
namespace GlobalPayments.Api.Entities {
77
internal class GpApiReportRequestBuilder {
88
internal static GpApiRequest BuildRequest<T>(ReportBuilder<T> builder, GpApiConnector gateway) where T : class {
9+
var merchantUrl = !string.IsNullOrEmpty(gateway.MerchantId) ? $"/merchants/{gateway.MerchantId}" : string.Empty;
910
if (builder is TransactionReportBuilder<T> trb) {
1011
var request = new GpApiRequest();
1112
switch (builder.ReportType) {
1213
case ReportType.TransactionDetail:
1314
return new GpApiRequest {
1415
Verb = HttpMethod.Get,
15-
Endpoint = $"/transactions/{trb.TransactionId}",
16+
Endpoint = $"{merchantUrl}/transactions/{trb.TransactionId}",
1617
};
1718
case ReportType.FindTransactionsPaged:
1819
request = new GpApiRequest {
1920
Verb = HttpMethod.Get,
20-
Endpoint = "/transactions",
21+
Endpoint = $"{merchantUrl}/transactions",
2122
};
2223
request.AddQueryStringParam("page", trb.Page?.ToString());
2324
request.AddQueryStringParam("page_size", trb.PageSize?.ToString());
@@ -49,7 +50,7 @@ internal static GpApiRequest BuildRequest<T>(ReportBuilder<T> builder, GpApiConn
4950
case ReportType.FindSettlementTransactionsPaged:
5051
request = new GpApiRequest {
5152
Verb = HttpMethod.Get,
52-
Endpoint = "/settlement/transactions",
53+
Endpoint = $"{merchantUrl}/settlement/transactions",
5354
};
5455
request.AddQueryStringParam("page", trb.Page?.ToString());
5556
request.AddQueryStringParam("page_size", trb.PageSize?.ToString());
@@ -79,12 +80,12 @@ internal static GpApiRequest BuildRequest<T>(ReportBuilder<T> builder, GpApiConn
7980
case ReportType.DepositDetail:
8081
return new GpApiRequest {
8182
Verb = HttpMethod.Get,
82-
Endpoint = $"/settlement/deposits/{trb.SearchBuilder.DepositReference}",
83+
Endpoint = $"{merchantUrl}/settlement/deposits/{trb.SearchBuilder.DepositReference}",
8384
};
8485
case ReportType.FindDepositsPaged:
8586
request = new GpApiRequest {
8687
Verb = HttpMethod.Get,
87-
Endpoint = "/settlement/deposits",
88+
Endpoint = $"{merchantUrl}/settlement/deposits",
8889
};
8990
request.AddQueryStringParam("page", trb.Page?.ToString());
9091
request.AddQueryStringParam("page_size", trb.PageSize?.ToString());
@@ -104,12 +105,12 @@ internal static GpApiRequest BuildRequest<T>(ReportBuilder<T> builder, GpApiConn
104105
case ReportType.DisputeDetail:
105106
return new GpApiRequest {
106107
Verb = HttpMethod.Get,
107-
Endpoint = $"/disputes/{trb.SearchBuilder.DisputeId}",
108+
Endpoint = $"{merchantUrl}/disputes/{trb.SearchBuilder.DisputeId}",
108109
};
109110
case ReportType.FindDisputesPaged:
110111
request = new GpApiRequest {
111112
Verb = HttpMethod.Get,
112-
Endpoint = "/disputes",
113+
Endpoint = $"{merchantUrl}/disputes",
113114
};
114115
request.AddQueryStringParam("page", trb.Page?.ToString());
115116
request.AddQueryStringParam("page_size", trb.PageSize?.ToString());
@@ -128,12 +129,12 @@ internal static GpApiRequest BuildRequest<T>(ReportBuilder<T> builder, GpApiConn
128129
case ReportType.SettlementDisputeDetail:
129130
return new GpApiRequest {
130131
Verb = HttpMethod.Get,
131-
Endpoint = $"/settlement/disputes/{trb.SearchBuilder.SettlementDisputeId}",
132+
Endpoint = $"{merchantUrl}/settlement/disputes/{trb.SearchBuilder.SettlementDisputeId}",
132133
};
133134
case ReportType.FindSettlementDisputesPaged:
134135
request = new GpApiRequest {
135136
Verb = HttpMethod.Get,
136-
Endpoint = "/settlement/disputes",
137+
Endpoint = $"{merchantUrl}/settlement/disputes",
137138
};
138139
request.AddQueryStringParam("account_name", gateway.DataAccountName);
139140
request.AddQueryStringParam("deposit_id", trb.SearchBuilder.DepositReference);
@@ -156,12 +157,12 @@ internal static GpApiRequest BuildRequest<T>(ReportBuilder<T> builder, GpApiConn
156157
case ReportType.StoredPaymentMethodDetail:
157158
return new GpApiRequest {
158159
Verb = HttpMethod.Get,
159-
Endpoint = $"/payment-methods/{trb.SearchBuilder.StoredPaymentMethodId}",
160+
Endpoint = $"{merchantUrl}/payment-methods/{trb.SearchBuilder.StoredPaymentMethodId}",
160161
};
161162
case ReportType.FindStoredPaymentMethodsPaged:
162163
request = new GpApiRequest {
163164
Verb = HttpMethod.Get,
164-
Endpoint = "/payment-methods",
165+
Endpoint = $"{merchantUrl}/payment-methods",
165166
};
166167
request.AddQueryStringParam("page", trb.Page?.ToString());
167168
request.AddQueryStringParam("page_size", trb.PageSize?.ToString());
@@ -180,12 +181,12 @@ internal static GpApiRequest BuildRequest<T>(ReportBuilder<T> builder, GpApiConn
180181
case ReportType.ActionDetail:
181182
return new GpApiRequest {
182183
Verb = HttpMethod.Get,
183-
Endpoint = $"/actions/{trb.SearchBuilder.ActionId}",
184+
Endpoint = $"{merchantUrl}/actions/{trb.SearchBuilder.ActionId}",
184185
};
185186
case ReportType.FindActionsPaged:
186187
request = new GpApiRequest {
187188
Verb = HttpMethod.Get,
188-
Endpoint = "/actions",
189+
Endpoint = $"{merchantUrl}/actions",
189190
};
190191
request.AddQueryStringParam("page", trb.Page?.ToString());
191192
request.AddQueryStringParam("page_size", trb.PageSize?.ToString());

‎src/GlobalPayments.Api/Entities/GpApi/GpApiSecure3DRequestBuilder.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace GlobalPayments.Api.Entities {
99
internal class GpApiSecure3DRequestBuilder {
1010
internal static GpApiRequest BuildRequest(Secure3dBuilder builder, GpApiConnector gateway) {
11+
var merchantUrl = !string.IsNullOrEmpty(gateway.MerchantId) ? $"/merchants/{gateway.MerchantId}" : string.Empty;
1112
if (builder.TransactionType == TransactionType.VerifyEnrolled) {
1213
var storedCredential = new JsonDoc()
1314
.Set("model", EnumConverter.GetMapping(Target.GP_API, builder.StoredCredential?.Type))
@@ -48,7 +49,7 @@ internal static GpApiRequest BuildRequest(Secure3dBuilder builder, GpApiConnecto
4849

4950
return new GpApiRequest {
5051
Verb = HttpMethod.Post,
51-
Endpoint = "/authentications",
52+
Endpoint = $"{merchantUrl}/authentications",
5253
RequestBody = data.ToString(),
5354
};
5455
}
@@ -206,7 +207,7 @@ internal static GpApiRequest BuildRequest(Secure3dBuilder builder, GpApiConnecto
206207

207208
return new GpApiRequest {
208209
Verb = HttpMethod.Post,
209-
Endpoint = $"/authentications/{builder.ServerTransactionId}/initiate",
210+
Endpoint = $"{merchantUrl}/authentications/{builder.ServerTransactionId}/initiate",
210211
RequestBody = data.ToString(),
211212
};
212213
}
@@ -220,7 +221,7 @@ internal static GpApiRequest BuildRequest(Secure3dBuilder builder, GpApiConnecto
220221

221222
return new GpApiRequest {
222223
Verb = HttpMethod.Post,
223-
Endpoint = $"/authentications/{builder.ServerTransactionId}/result",
224+
Endpoint = $"{merchantUrl}/authentications/{builder.ServerTransactionId}/result",
224225
RequestBody = data?.ToString()
225226
};
226227
}

‎src/GlobalPayments.Api/Gateways/GpApiConnector.cs

+16-8
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ internal partial class GpApiConnector : RestGateway, IPaymentGateway, IReporting
2121
public string Country { get; set; }
2222
public string[] Permissions { get; set; }
2323
public string MerchantContactUrl { get; set; }
24-
24+
public string MerchantId { get; set; }
2525

2626
private string _AccessToken;
2727
public string AccessToken {
@@ -118,13 +118,21 @@ private string getReleaseVersion()
118118
}
119119

120120
public void SignIn() {
121-
var response = GetAccessToken();
122-
123-
AccessToken = response.Token;
124-
DataAccountName = response.DataAccountName;
125-
DisputeManagementAccountName = response.DisputeManagementAccountName;
126-
TokenizationAccountName = response.TokenizationAccountName;
127-
TransactionProcessingAccountName = response.TransactionProcessingAccountName;
121+
if (string.IsNullOrEmpty(_AccessToken))
122+
{
123+
var response = GetAccessToken();
124+
125+
AccessToken = response.Token;
126+
127+
if (string.IsNullOrEmpty(_DataAccountName))
128+
DataAccountName = response.DataAccountName;
129+
if (string.IsNullOrEmpty(_DisputeManagementAccountName))
130+
DisputeManagementAccountName = response.DisputeManagementAccountName;
131+
if (string.IsNullOrEmpty(_TokenizationAccountName))
132+
TokenizationAccountName = response.TokenizationAccountName;
133+
if (string.IsNullOrEmpty(_TransactionProcessingAccountName))
134+
TransactionProcessingAccountName = response.TransactionProcessingAccountName;
135+
}
128136
}
129137

130138
public void SignOut() {

‎src/GlobalPayments.Api/PaymentMethods/Credit.cs

+12-6
Original file line numberDiff line numberDiff line change
@@ -147,30 +147,36 @@ public Transaction GetTokenInformation(string configName = "default") {
147147
/// with the issuer in the process.
148148
/// </summary>
149149
/// <returns>AuthorizationBuilder</returns>
150-
public string Tokenize(string configName = "default", PaymentMethodUsageMode paymentMethodUsageMode = PaymentMethodUsageMode.Multiple) {
150+
public string Tokenize(string configName = "default", PaymentMethodUsageMode paymentMethodUsageMode = PaymentMethodUsageMode.Multiple)
151+
{
151152
return Tokenize(true, configName, paymentMethodUsageMode);
152153
}
153-
public string Tokenize(bool verifyCard, string configName = "default", PaymentMethodUsageMode paymentMethodUsageMode = PaymentMethodUsageMode.Multiple) {
154+
155+
public string Tokenize(bool verifyCard, string configName = "default", PaymentMethodUsageMode paymentMethodUsageMode = PaymentMethodUsageMode.Multiple)
156+
{
154157
TransactionType type = verifyCard ? TransactionType.Verify : TransactionType.Tokenize;
155158

156-
var response = new AuthorizationBuilder(type, this)
159+
var response = new AuthorizationBuilder(type, this)
157160
.WithRequestMultiUseToken(verifyCard)
158161
.WithPaymentMethodUsageMode(paymentMethodUsageMode)
159162
.Execute(configName);
160163
return response.Token;
161164
}
162165

163-
public string Tokenize(bool verifyCard, Address billingAddress, Customer customerData, string configName = "default") {
166+
public string Tokenize(bool verifyCard, Address billingAddress, Customer customerData, string configName = "default")
167+
{
164168
TransactionType type = verifyCard ? TransactionType.Verify : TransactionType.Tokenize;
165169

166170
var builder = new AuthorizationBuilder(type, this)
167171
.WithRequestMultiUseToken(verifyCard)
168172
.WithPaymentMethodUsageMode(PaymentMethodUsageMode.Multiple);
169173

170-
if (billingAddress != null) {
174+
if (billingAddress != null)
175+
{
171176
builder = builder.WithAddress(billingAddress);
172177
}
173-
if (customerData != null) {
178+
if (customerData != null)
179+
{
174180
builder = builder.WithCustomerData(customerData);
175181
}
176182

‎src/GlobalPayments.Api/Properties/AssemblyInfo.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("1.8.4")]
36-
[assembly: AssemblyFileVersion("1.8.4")]
35+
[assembly: AssemblyVersion("1.8.5")]
36+
[assembly: AssemblyFileVersion("1.8.5")]

‎src/GlobalPayments.Api/ServiceConfigs/Gateways/GpApiConfig.cs

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public class GpApiConfig : GatewayConfig {
5555
/// </summary>
5656
public string MerchantContactUrl { get; set; }
5757

58+
public string MerchantId { get; set; }
59+
5860
public GpApiConfig() : base(GatewayProvider.GP_API) { }
5961

6062
internal override void ConfigureContainer(ConfiguredServices services) {
@@ -87,6 +89,7 @@ internal override void ConfigureContainer(ConfiguredServices services) {
8789
MerchantContactUrl = MerchantContactUrl,
8890
WebProxy = WebProxy,
8991
DynamicHeaders = DynamicHeaders,
92+
MerchantId = MerchantId
9093
};
9194

9295
services.GatewayConnector = gateway;

‎tests/GlobalPayments.Api.Tests/GpApi/GpApiCreditWithMerchantIdTests.cs

+979
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.